安卓:Fragment的懒加载
2017-10-18 20:58
155 查看
Android应用开发过程中,ViewPager同时加载多个fragment,以实现多tab页面快速切换, 但是fragment初始化时若加载的内容较多,就可能导致整个应用启动速度缓慢,影响用户体验。
为了提高用户体验,我们会使用一些懒加载方案,实现加载延迟。这时我们会用到getUserVisibleHint()与setUserVisibleHint()这两个方法。
Fragment生命周期执行流程:
Fragment创建:setUserVisibleHint()->onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume();
Fragment变为不可见状态(锁屏、回到桌面、被Activity完全覆盖):onPause()->onSaveInstanceState()->onStop();
Fragment变为部分可见状态(打开Dialog样式的Activity):onPause()->onSaveInstanceState();
Fragment由不可见变为活动状态:onStart()->OnResume();
Fragment由部分可见变为活动状态:onResume();
退出应用:onPause()->onStop()->onDestroyView()->onDestroy()->onDetach()(注意退出不会调用onSaveInstanceState方法,因为是人为退出,没有必要再保存数据);
Fragment被回收又重新创建:被回收执行onPause()->onSaveInstanceState()->onStop()->onDestroyView()->onDestroy()->onDetach(),重新创建执行onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()->setUserVisibleHint();
横竖屏切换:与Fragment被回收又重新创建一样。
从上述源码注释我们可以看出,当fragment被用户可见时,setUserVisibleHint()会调用且传入true值,当fragment不被用户可见时,setUserVisibleHint()则得到false值。而在传统的fragment生命周期里也看不到这个函数。
那么,问题来了,
fragment是如何知道自己时候用户可见?
setUserVisibleHint() 在上图所示fragment的生命周期的什么位置?
先说结论:
viewpager监听切换tab事件,tab切换一次,执行一次setUserVisibleHint()方法
setUserVisibleHint() 在 上图所示fragment所有生命周期之前,无论viewpager是在activity哪个生命周期里初始化。
activity生命周期 和 fragment生命周期 时序并不是按序来的,也就是说fragment的oncreate方法时序并不一定在activity的oncreate方法之后。
具体原因,我们从应用场景开始一点一点的分析。
/frameworks/support/v4/java/android/support/v4/app/FragmentManager.java
为了提高用户体验,我们会使用一些懒加载方案,实现加载延迟。这时我们会用到getUserVisibleHint()与setUserVisibleHint()这两个方法。
声明周期
首先先从Fragment的声明周期开始说起,讲一讲Fragment每个生命周期方法的意义、作用:setUserVisibleHint():
设置Fragment可见或者不可见时会调用此方法。在该方法里面可以通过调用getUserVisibleHint()获得Fragment的状态是可见还是不可见的,如果可见则进行懒加载操作。onAttach():
执行该方法时,Fragment与Activity已经完成绑定,该方法有一个Activity类型的参数,代表绑定的Activity,这时候你可以执行诸如mActivity = activity的操作。onCreate():
初始化Fragment。可通过参数savedInstanceState获取之前保存的值。onCreateView():
初始化Fragment的布局。加载布局和findViewById的操作通常在此函数内完成,但是不建议执行耗时的操作,比如读取数据库数据列表。onActivityCreated():
执行该方法时,与Fragment绑定的Activity的onCreate方法已经执行完成并返回,在该方法内可以进行与Activity交互的UI操作,所以在该方法之前Activity的onCreate方法并未执行完成,如果提前进行交互操作,会引发空指针异常。onStart():
执行该方法时,Fragment由不可见变为可见状态。onResume():
执行该方法时,Fragment处于活动状态,用户可与之交互。onPause():
执行该方法时,Fragment处于暂停状态,但依然可见,用户不能与之交互。onSaveInstanceState():
保存当前Fragment的状态。该方法会自动保存Fragment的状态,比如EditText键入的文本,即使Fragment被回收又重新创建,一样能恢复EditText之前键入的文本。onStop():
执行该方法时,Fragment完全不可见。onDestroyView():
销毁与Fragment有关的视图,但未与Activity解除绑定,依然可以通过onCreateView方法重新创建视图。通常在ViewPager+Fragment的方式下会调用此方法。onDestroy():
销毁Fragment。通常按Back键退出或者Fragment被回收时调用此方法。onDetach():
解除与Activity的绑定。在onDestroy方法之后调用。Fragment生命周期执行流程:
Fragment创建:setUserVisibleHint()->onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume();
Fragment变为不可见状态(锁屏、回到桌面、被Activity完全覆盖):onPause()->onSaveInstanceState()->onStop();
Fragment变为部分可见状态(打开Dialog样式的Activity):onPause()->onSaveInstanceState();
Fragment由不可见变为活动状态:onStart()->OnResume();
Fragment由部分可见变为活动状态:onResume();
退出应用:onPause()->onStop()->onDestroyView()->onDestroy()->onDetach()(注意退出不会调用onSaveInstanceState方法,因为是人为退出,没有必要再保存数据);
Fragment被回收又重新创建:被回收执行onPause()->onSaveInstanceState()->onStop()->onDestroyView()->onDestroy()->onDetach(),重新创建执行onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()->setUserVisibleHint();
横竖屏切换:与Fragment被回收又重新创建一样。
看一下源码
/** * * @param isVisibleToUser true if this fragment's UI is currently visible to the user (default), * false if it is not. */ public void setUserVisibleHint(boolean isVisibleToUser) { if (!mUserVisibleHint && isVisibleToUser && mState < STARTED) { mFragmentManager.performPendingDeferredStart(this); } mUserVisibleHint = isVisibleToUser; mDeferStart = !isVisibleToUser; } /** * @return The current value of the user-visible hint on this fragment. * @see #setUserVisibleHint(boolean) */ public boolean getUserVisibleHint() { return mUserVisibleHint; }
从上述源码注释我们可以看出,当fragment被用户可见时,setUserVisibleHint()会调用且传入true值,当fragment不被用户可见时,setUserVisibleHint()则得到false值。而在传统的fragment生命周期里也看不到这个函数。
那么,问题来了,
fragment是如何知道自己时候用户可见?
setUserVisibleHint() 在上图所示fragment的生命周期的什么位置?
先说结论:
viewpager监听切换tab事件,tab切换一次,执行一次setUserVisibleHint()方法
setUserVisibleHint() 在 上图所示fragment所有生命周期之前,无论viewpager是在activity哪个生命周期里初始化。
activity生命周期 和 fragment生命周期 时序并不是按序来的,也就是说fragment的oncreate方法时序并不一定在activity的oncreate方法之后。
具体原因,我们从应用场景开始一点一点的分析。
1. 我们的使用场景:
public class MainActivity extends FragmentActivity { private ViewPager viewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); viewPager = (ViewPager) findViewById(R.id.viewpager); vpOrder.setAdapter(new MainFragmentPagerAdapter(getSupportFragmentManager())); vpOrder.setOffscreenPageLimit(5); vpOrder.setCurrentItem(0); } }
2.ViewPager ,FragmentPagerAdapter
/frameworks/base/core/java/android/app/Activity.java//每次切换ViewPager的Tab时调用的方法 void populate(int newCurrentItem) { mAdapter.startUpdate(this); //...... addNewItem(mCurItem, curIndex); // mCurItem 为当前可见Fragment // 调用setUserVisibleHint(true) mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null); mAdapter.finishUpdate(this); //..... } ItemInfo addNewItem(int position, int index) { ItemInfo ii = new ItemInfo(); ii.position = position; //初始化fragment, 调用setUserVisibleHint(false) ii.object = mAdapter.instantiateItem(this, position); ii.widthFactor = mAdapter.getPageWidth(position); if (index < 0 || index >= mItems.size()) { mItems.add(ii); } else { mItems.add(index, ii); } return ii; }
3. Activity , FragmentManager
/frameworks/support/v4/java/android/support/v4/app/FragmentManager.java@Override public Object instantiateItem(ViewGroup container, int p ce88 osition) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } final long itemId = getItemId(position); // Do we already have this fragment? String name = makeFragmentName(container.getId(), itemId); Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment); } else { fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); //将fragment添加到FragmentManager里面 mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); //我们要找的方法在这里 fragment.setUserVisibleHint(false); } return fragment; } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment)object; if (fragment != mCurrentPrimaryItem) { if (mCurrentPrimaryItem != null) { mCurrentPrimaryItem.setMenuVisibility(false); mCurrentPrimaryItem.setUserVisibleHint(false); } if (fragment != null) { //我们要找的方法在这里 fragment.setMenuVisibility(true); fragment.setUserVisibleHint(true); } mCurrentPrimaryItem = fragment; } }
/frameworks/support/v4/java/android/support/v4/app/FragmentManager.java
//分发onCreate事件函数 public void dispatchCreate() { mStateSaved = false; moveToState(Fragment.CREATED, false); } //moveToState 重载 1 void moveToState(int newState, boolean always) { moveToState(newState, 0, 0, always); } //moveToState 重载 2 void moveToState(int newState, int transit, int transitStyle, boolean always) { if (mHost == null && newState != Fragment.INITIALIZING) { throw new IllegalStateException("No host"); } if (!always && mCurState == newState) { return; } mCurState = newState; //若mActive为null,就算Activtiy里面调用了dispatchOnCreate()也不会执行Fragment //的OnAttach和onCreate等方法。 //只有mActive非null,即addFragment()执行后,才会真正进入到生命周期。 //而根据FragmentPagerAdapter可知,只有当viewpager调用setAdapter方法,才会添加fragment到FramentManager。 //执行setAdapter的时候,会调用setUserVisibleHint()方法,并且,只有当setAdapter方法执行完之后,才会进入到Fragment到生命周期,因此setUserVisibleHint()方法在所有生命周期之前被调用。 if (mActive != null) { boolean loadersRunning = false; for (int i=0; i<mActive.size(); i++) { Fragment f = mActive.get(i); if (f != null) { moveToState(f, newState, transit, transitStyle, false); if (f.mLoaderManager != null) { loadersRunning |= f.mLoaderManager.hasRunningLoaders(); } } } if (!loadersRunning) { startPendingDeferredFragments(); } if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) { mHost.onSupportInvalidateOptionsMenu(); mNeedMenuInvalidate = false; } } } //moveToState 重载 3 void moveToState(Fragment f, int newState, int transit, int transitionStyle, //... f.onAttach(); //... f.performOnCreate(); //其他生命周期 } //添加fragment到FragmentManager public void addFragment(Fragment fragment, boolean moveToStateNow) { if (mAdded == null) { mAdded = new ArrayList<Fragment>(); } if (DEBUG) Log.v(TAG, "add: " + fragment); //激活fragment makeActive(fragment); if (!fragment.mDetached) { if (mAdded.contains(fragment)) { throw new IllegalStateException("Fragment already added: " + fragment); } mAdded.add(fragment); fragment.mAdded = true; fragment.mRemoving = false; if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } if (moveToStateNow) { moveToState(fragment); } } } void makeActive(Fragment f) { if (f.mIndex >= 0) { return; } if (mAvailIndices == null || mAvailIndices.size() <= 0) { if (mActive == null) { //激活Fragment mActive = new ArrayList<Fragment>(); } f.setIndex(mActive.size(), mParent); mActive.add(f); } else { f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent); mActive.set(f.mIndex, f); } if (DEBUG) Log.v(TAG, "Allocated fragment index " + f); }
相关文章推荐
- 安卓在一个活动(Activity)里加载碎片(Fragment)并切换到另一个碎片(Fragment)
- 安卓在一个活动(Activity)里加载碎片(Fragment)并切换到另一个碎片(Fragment)
- 安卓 碎片fragment的懒加载 fragment只加载一次 用到的时候才加载
- !!!!!安卓界面总是显示加载进度条的问题,没事别乱用ListFragment
- 自学安卓复习基础_之十一(在Activity中加载fragment的页面)
- 【安卓-自定义布局】安卓App开发思路 一步一个脚印(十四)实现Fragment的缓冲视图和数据,防止重复加载
- 安卓 fragment 懒加载
- 安卓Fragment资源懒加载
- 安卓在一个活动(Activity)里加载碎片(Fragment)并切换到另一个碎片(Fragment)
- 安卓在一个活动(Activity)里加载碎片(Fragment)并切换到另一个碎片(Fragment)
- XlistView的上拉刷新下拉加载 和Fragment 和无限轮播
- 安卓图片加载之使用universalimageloader加载圆形圆角图片
- 安卓开发笔记——Fragment+FragmentTabHost组件(实现新浪微博底部菜单)
- Fragment取消预加载
- 安卓第十天笔记-fragment
- Fragment的setUserVisibleHint方法实现延时加载
- 安卓开发复习笔记——Fragment+FragmentTabHost组件(实现新浪微博底部菜单)
- 安卓 加载图片并在上面画图
- Android仿微信Viewpager-Fragment惰性加载(lazy-loading)
- Viewpager中的Fragment第一次点击时加载,然后保存其状态