前言
在阅读ViewPager源码的过程中,偶然间发现Fragment#setUserVisibleHint()方法过时,所以带着好奇看了下注释如下:
/**
* Set a hint to the system about whether this fragment's UI is currently visible
* to the user. This hint defaults to true and is persistent across fragment instance
* state save and restore.
*
* An app may set this to false to indicate that the fragment's UI is
* scrolled out of visibility or is otherwise not directly visible to the user.
* This may be used by the system to prioritize operations such as fragment lifecycle updates
* or loader ordering behavior.
*
* Note: This method may be called outside of the fragment lifecycle.
* and thus has no ordering guarantees with regard to fragment lifecycle method calls.
*
* @param isVisibleToUser true if this fragment's UI is currently visible to the user (default),
* false if it is not.
*
* @deprecated Use {@link FragmentTransaction#setMaxLifecycle(Fragment, Lifecycle.State)}
* instead.
*/
使用setMaxLifecycle()代替,因此产生了这篇文章。
发现点
当我阅读到ViewPager#populate()计算滑动距离的方法时,最终会调用一个setPrimaryItem()方法
void populate(int newCurrentItem) {
//... 省略超级多的代码
calculatePageOffsets(curItem, curIndex, oldCurInfo);
mAdapter.setPrimaryItem(this, mCurItem, curItem.object);
}
跟进看下,发现一个PagerAdapter抽象类,我们很熟知了,在我们使用VP嵌套Fragment时,经常使用它派生出的两个子类作为适配器。
这两个派生出的子类中实现的setPrimaryItem()实现方式相同,我就以FragmentPagerAdapter为例。
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
} else {
mCurrentPrimaryItem.setUserVisibleHint(false);
}
}
fragment.setMenuVisibility(true);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
} else {
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
会发现在配置事务时,会先去if判断一下mBehavior,是否是BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,若当前的fragment为空,则setMaxLifecycle(Lifecycle.State.STARTED), 若已经创建过了,则setMaxLifecycle(fragment, Lifecycle.State.RESUMED),否则会去兼容老的setUserVisibleHint()逻辑。由前言可见setUserVisibleHint()方法过时了,因此好奇,这个setMaxLifecycle()是什么东东?
FragmentTransaction#setMaxLifecycle()
@NonNull
public FragmentTransaction setMaxLifecycle(@NonNull Fragment fragment,
@NonNull Lifecycle.State state) {
addOp(new Op(OP_SET_MAX_LIFECYCLE, fragment, state));
return this;
}
由此发现,在提交事务时,将开发者设置的LifeCycle#State枚举值存入了Op中,在最终提交事务时,根据这个State枚举去判断调用目标Fragment相关的生命周期方法,这个详细流程由于篇幅原因就不追源码了。之后我们看下这个枚举。
/**
* Lifecycle states. You can consider the states as the nodes in a graph and
* {@link Event}s as the edges between these nodes.
*/
@SuppressWarnings("WeakerAccess")
public enum State {
/**
* Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
* any more events. For instance, for an {@link android.app.Activity}, this state is reached
* right before Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
*/
DESTROYED,
/**
* Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
* the state when it is constructed but has not received
* {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
*/
INITIALIZED,
/**
* Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached in two cases:
*
* - after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
*
- right before {@link android.app.Activity#onStop() onStop} call.
*
*/
CREATED,
/**
* Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached in two cases:
*
* - after {@link android.app.Activity#onStart() onStart} call;
*
- right before {@link android.app.Activity#onPause() onPause} call.
*
*/
STARTED,
/**
* Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached after {@link android.app.Activity#onResume() onResume} is called.
*/
RESUMED;
/**
* Compares if this State is greater or equal to the given {@code state}.
*
* @param state State to compare with
* @return true if this State is greater or equal to the given {@code state}
*/
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
}
我们依次来看下。
- 不设置
getSupportFragmentManager().beginTransaction()
.add(R.id.fl_container, mInvoiceManagerFragment)
.commit();
- CREATED
getSupportFragmentManager().beginTransaction()
.add(R.id.fl_container, mInvoiceManagerFragment)
.setMaxLifecycle(mInvoiceManagerFragment, State.CREATED)
.commit();
- STARTED
getSupportFragmentManager().beginTransaction()
.add(R.id.fl_container, mInvoiceManagerFragment)
.setMaxLifecycle(mInvoiceManagerFragment,State.STARTED)
.commit();
- RESUMED
getSupportFragmentManager().beginTransaction()
.add(R.id.fl_container, mInvoiceManagerFragment)
.setMaxLifecycle(mInvoiceManagerFragment,State.RESUMED)
.commit();
所以 在设置最大周期后,最终Fragment生命周期变的可控。总结一下,
static final int INITIALIZING = 0; // 还没有创建,初始状态
static final int CREATED = 1; // 已创建
static final int ACTIVITY_CREATED = 2; // 完全创建但是还没有start
static final int STARTED = 3; // 创建并启动,可见但是不能操作
static final int RESUMED = 4; // 创建启动并且可以操作
Fragment所有的生命周期顺序:
onCreate->onCretateView->onStart->onResume->onPause->onStop-> onDestoryView->onDestory
CREATED->STARTED->RESUMED
根据注释中的LifeCycle状态注释,我们可以知道
- CREATED
CREATED即已创建状态,生命周期方法走到onCreate,如果当前fragment状态已大于CREATED,则会使fragment生命周期方法走到onDestoryView,如果小于CREATED,则走到onCreate。 - STARTED
如果当前fragment状态已大于STARTED,则会使fragment生命周期方法走到onPause,如果小于STARTED,则走到onStart; - RESUMED
无论什么情况,都只调用到onResume;
ViewPager 懒加载
- FragmentStatePagerAdapter
private static class FragmentPagerAdapter extends FragmentStatePagerAdapter {
private final ArrayList mFragments;
private final ArrayList mFragmentTitles;
FragmentPagerAdapter(FragmentManager fm, ArrayList fragmentTitles, ArrayList fragments) {
super(fm, FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.mFragments = fragments;
this.mFragmentTitles = fragmentTitles;
}
@Override
public int getCount() {
return mFragments.size() == mFragmentTitles.size() ? mFragments.size() : 0;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitles.get(position);
}
@NonNull
@Override
public Fragment getItem(int i) {
return mFragments.get(i);
}
@Override
public int getItemPosition(@NonNull Object object) {
return POSITION_NONE;
}
}
构造方法中传入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT这个标记,其中有两个标记,默认为BEHAVIOR_SET_USER_VISIBLE_HINT,setUserVisible()方法是正常被调用的。
/**
* Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current
* fragment changes.
*
* @deprecated This behavior relies on the deprecated
* {@link Fragment#setUserVisibleHint(boolean)} API. Use
* {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,
* {@link FragmentTransaction#setMaxLifecycle}.
* @see #FragmentStatePagerAdapter(FragmentManager, int)
*/
@Deprecated
public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
/**
* Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}
* state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.
*
* @see #FragmentStatePagerAdapter(FragmentManager, int)
*/
public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
- BaseFragment
public class BaseFragment extends Fragment{
private boolean isFirst = true;
private boolean isCreate = false;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
isCreate = true;
}
@Override
public void onResume() {
super.onResume();
// 在onResume中进行数据懒加载
initLoadData();
}
private void initLoadData() {
if (isCreate && isFirst) {
requestData();
isFirst = !isFirst;
}
}
}