您的位置:首页 > 移动开发 > Android开发

安卓:Fragment的懒加载

2017-10-18 20:58 155 查看
Android应用开发过程中,ViewPager同时加载多个fragment,以实现多tab页面快速切换, 但是fragment初始化时若加载的内容较多,就可能导致整个应用启动速度缓慢,影响用户体验。

为了提高用户体验,我们会使用一些懒加载方案,实现加载延迟。这时我们会用到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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android fragment 懒加载