AndroidX Fragment 感知生命周期

前言

在閱讀ViewPager源碼的過程中,偶然間發(fā)現(xiàn)Fragment#setUserVisibleHint()方法過時(shí),所以帶著好奇看了下注釋如下:

/**
 * 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()代替,因此產(chǎn)生了這篇文章。

發(fā)現(xiàn)點(diǎn)

當(dāng)我閱讀到ViewPager#populate()計(jì)算滑動(dòng)距離的方法時(shí),最終會(huì)調(diào)用一個(gè)setPrimaryItem()方法

void populate(int newCurrentItem) {
    //... 省略超級(jí)多的代碼
    calculatePageOffsets(curItem, curIndex, oldCurInfo);
    mAdapter.setPrimaryItem(this, mCurItem, curItem.object);
}

跟進(jìn)看下,發(fā)現(xiàn)一個(gè)PagerAdapter抽象類,我們很熟知了,在我們使用VP嵌套Fragment時(shí),經(jīng)常使用它派生出的兩個(gè)子類作為適配器。


PagerAdapter.png

這兩個(gè)派生出的子類中實(shí)現(xiàn)的setPrimaryItem()實(shí)現(xiàn)方式相同,我就以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;
    }
}

會(huì)發(fā)現(xiàn)在配置事務(wù)時(shí),會(huì)先去if判斷一下mBehavior,是否是BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,若當(dāng)前的fragment為空,則setMaxLifecycle(Lifecycle.State.STARTED), 若已經(jīng)創(chuàng)建過了,則setMaxLifecycle(fragment, Lifecycle.State.RESUMED),否則會(huì)去兼容老的setUserVisibleHint()邏輯。由前言可見setUserVisibleHint()方法過時(shí)了,因此好奇,這個(gè)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;
}

由此發(fā)現(xiàn),在提交事務(wù)時(shí),將開發(fā)者設(shè)置的LifeCycle#State枚舉值存入了Op中,在最終提交事務(wù)時(shí),根據(jù)這個(gè)State枚舉去判斷調(diào)用目標(biāo)Fragment相關(guān)的生命周期方法,這個(gè)詳細(xì)流程由于篇幅原因就不追源碼了。之后我們看下這個(gè)枚舉。

/**
 * 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;
    }
}

我們依次來看下。

  • 不設(shè)置
getSupportFragmentManager().beginTransaction()
                    .add(R.id.fl_container, mInvoiceManagerFragment)
                    .commit();
不設(shè)置
  • 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

所以 在設(shè)置最大周期后,最終Fragment生命周期變的可控。總結(jié)一下,

static final int INITIALIZING = 0;     // 還沒有創(chuàng)建,初始狀態(tài)
static final int CREATED = 1;          // 已創(chuàng)建
static final int ACTIVITY_CREATED = 2; // 完全創(chuàng)建但是還沒有start
static final int STARTED = 3;          // 創(chuàng)建并啟動(dòng),可見但是不能操作
static final int RESUMED = 4;          // 創(chuàng)建啟動(dòng)并且可以操作

Fragment所有的生命周期順序:

onCreate->onCretateView->onStart->onResume->onPause->onStop-> onDestoryView->onDestory

CREATED->STARTED->RESUMED

根據(jù)注釋中的LifeCycle狀態(tài)注釋,我們可以知道

  • CREATED
    CREATED即已創(chuàng)建狀態(tài),生命周期方法走到onCreate,如果當(dāng)前fragment狀態(tài)已大于CREATED,則會(huì)使fragment生命周期方法走到onDestoryView,如果小于CREATED,則走到onCreate。
  • STARTED
    如果當(dāng)前fragment狀態(tài)已大于STARTED,則會(huì)使fragment生命周期方法走到onPause,如果小于STARTED,則走到onStart;
  • RESUMED
    無(wú)論什么情況,都只調(diào)用到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;
    }
}

構(gòu)造方法中傳入BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT這個(gè)標(biāo)記,其中有兩個(gè)標(biāo)記,默認(rèn)為BEHAVIOR_SET_USER_VISIBLE_HINT,setUserVisible()方法是正常被調(diào)用的。

/**
 * 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中進(jìn)行數(shù)據(jù)懶加載
        initLoadData();
    }
   
    private void initLoadData() {
        if (isCreate && isFirst) {
            requestData();
            isFirst = !isFirst;
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,778評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,795評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,993評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,229評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,687評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,990評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,592評(píng)論 0 11
  • 彩排完,天已黑
    劉凱書法閱讀 4,260評(píng)論 1 3
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 125,539評(píng)論 2 7