AndroidX Fragment 感知生命周期

前言

在阅读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.
 *
 * <p>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.</p>
 *
 * <p><strong>Note:</strong> This method may be called outside of the fragment lifecycle.
 * and thus has no ordering guarantees with regard to fragment lifecycle method calls.</p>
 *
 * @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时,经常使用它派生出的两个子类作为适配器。


PagerAdapter.png

这两个派生出的子类中实现的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
     * <b>right before</b> 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:
     * <ul>
     *     <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
     *     <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
     * </ul>
     */
    CREATED,
    /**
     * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
     * is reached in two cases:
     * <ul>
     *     <li>after {@link android.app.Activity#onStart() onStart} call;
     *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
     * </ul>
     */
    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();
CREATED
  • STARTED
getSupportFragmentManager().beginTransaction()
        .add(R.id.fl_container, mInvoiceManagerFragment)
        .setMaxLifecycle(mInvoiceManagerFragment,State.STARTED)
        .commit();
STARTED
  • RESUMED
getSupportFragmentManager().beginTransaction()
        .add(R.id.fl_container, mInvoiceManagerFragment)
        .setMaxLifecycle(mInvoiceManagerFragment,State.RESUMED)
        .commit();
RESUMED

所以 在设置最大周期后,最终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<EasyMockDataFragment> mFragments;
    private final ArrayList<String> mFragmentTitles;
    FragmentPagerAdapter(FragmentManager fm, ArrayList<String> fragmentTitles, ArrayList<EasyMockDataFragment> 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;
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,548评论 0 11
  • 彩排完,天已黑
    刘凯书法阅读 4,182评论 1 3
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 123,998评论 2 7