Framgent中使用FragmentManager遇到的問題

ViewPager適配器中FragmentManager的選擇


在我們使用ViewPager的過程中都需要傳入一個FragmentManager媳溺,至于FragmentManager該怎么選擇呢,相信大部分人都知道碍讯。在Activity中傳入supportFragmentManager悬蔽,在fragment中則使用childFragmentManager。

有同事不信邪偏偏在fragment中使用時傳入對應的activity的supportFragmentManager捉兴,像下面這樣

mTitles.add("第一頁")

mTitles.add("第二頁")

mFragments.add(NumberFragment())

mFragments.add(NumberFragment())

val adapter = MyAdapter(activity!!.supportFragmentManager)

mViewPager.adapter = adapter

mTabLayout.setupWithViewPager(mViewPager)

inner class MyAdapter(fm: FragmentManager) : FragmentPagerAdapter(

        fm,

        BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT

    ) {

        override fun getItem(position: Int) = mFragments[position]

        override fun getCount() = mFragments.size

        override fun getPageTitle(position: Int): CharSequence? {

            return mTitles[position]

        }

        override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {

            super.destroyItem(container, position, `object`)

        }

    }

結(jié)果就出現(xiàn)了下面這種情況

image

為什么會出現(xiàn)這種情況呢蝎困,其實很好理解,主要就是生命周期管理的問題倍啥,傳入了activity的supportFragmentManager就會在fragment創(chuàng)建時為Fragment中的mFragmentManager賦值為supportFragmentManager

本身fragment中嵌套的fragment的生命周期是由上層的fragment管理禾乘,現(xiàn)在變成activity來管理,這就造成了fragment該被銷毀時無法被銷毀逗栽。

從下面的log日志中我們也能看出viewpager中的fragment沒有被正常銷毀,onDestroyView沒有被正常調(diào)用

2020-04-10 13:40:36.725 17630-17630/com.rxy.selfapp E/Peter: NumberFragment load onCreateView

2020-04-10 13:40:36.728 17630-17630/com.rxy.selfapp E/Peter: NumberFragment load onCreateView

那為什么沒有被銷毀就會出現(xiàn)這種情況呢,通過跟蹤源碼我們可以發(fā)現(xiàn)每次重新切回fragment時ViewPager的setAdapter方法就會被調(diào)用

/**

    * Set a PagerAdapter that will supply views for this pager as needed.

    *

    * @param adapter Adapter to use

    */

    public void setAdapter(@Nullable PagerAdapter adapter) {

      ........省略

        final PagerAdapter oldAdapter = mAdapter;

        mAdapter = adapter;

        mExpectedAdapterCount = 0;

        if (mAdapter != null) {

            if (mObserver == null) {

                mObserver = new PagerObserver();

            }

            mAdapter.setViewPagerObserver(mObserver);

            mPopulatePending = false;

            final boolean wasFirstLayout = mFirstLayout;

            mFirstLayout = true;

            mExpectedAdapterCount = mAdapter.getCount();

        .......省略

    }

最終都會走到

void populate(int newCurrentItem){

........省略

if (curItem == null && N > 0) {

    curItem = addNewItem(mCurItem, curIndex);

}

.......省略

}

下面是addNewItem方法的具體實現(xiàn)

ItemInfo addNewItem(int position, int index) {

        ItemInfo ii = new ItemInfo();

        ii.position = position;

        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;

    }

最終會走到adapter中的instantiateItem()方法中

    @NonNull

    @Override

    public Object instantiateItem(@NonNull ViewGroup container, int position) {

    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);

            mCurTransaction.add(container.getId(), fragment,

                    makeFragmentName(container.getId(), itemId));

        }

        if (fragment != mCurrentPrimaryItem) {

            fragment.setMenuVisibility(false);

            if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {

                mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);

            } else {

                fragment.setUserVisibleHint(false);

            }

        }

        return fragment;

    }

而通過對比我們會發(fā)現(xiàn)當adapter傳入activity的supportFragmentManager時 mFragmentManager.findFragmentByTag(name)不為空會走 mCurTransaction.attach()方法失暂,而傳入fragment的childFragmentManager時mFragmentManager.findFragmentByTag(name)為空會走 mCurTransaction.add()方法彼宠,那這兩種方法有什么區(qū)別呢鳄虱?

看源碼我們會發(fā)現(xiàn),當FragmentManager調(diào)用beginTransaction方法時會返回一個BackStackRecord對象凭峡,而BackStackRecord則是FragmentTransaction的一個子類拙已,而無論是attach方法還是add方法最終都會走到BackStackRecord中的executeOps方法。

/**

    * Executes the operations contained within this transaction. The Fragment states will only

    * be modified if optimizations are not allowed.

    */

    void executeOps() {

        ....省略若干

            switch (op.mCmd) {

                case OP_ADD:

                    f.setNextAnim(op.mEnterAnim);

                    mManager.setExitAnimationOrder(f, false);

                    mManager.addFragment(f);

                    break;

                case OP_ATTACH:

                    f.setNextAnim(op.mEnterAnim);

                    mManager.setExitAnimationOrder(f, false);

                    mManager.attachFragment(f);

                    break;

                default:

                    throw new IllegalArgumentException("Unknown cmd: " + op.mCmd);

            }

            ....省略若干

    }

我們發(fā)現(xiàn)add和attacth最終都會調(diào)用mManager的addFragment和attachFragment中摧冀,mManager則是我們傳入的FragmentManager倍踪,下面我們看看attachFragment的具體實現(xiàn),方法在FragmentManager中

void attachFragment(@NonNull Fragment fragment) {

        if (isLoggingEnabled(Log.VERBOSE)) Log.v(TAG, "attach: " + fragment);

        if (fragment.mDetached) {

            fragment.mDetached = false;

            if (!fragment.mAdded) {

                mFragmentStore.addFragment(fragment);

                if (isLoggingEnabled(Log.VERBOSE)) Log.v(TAG, "add from attach: " + fragment);

                if (isMenuAvailable(fragment)) {

                    mNeedMenuInvalidate = true;

                }

            }

        }

    }

只有當fragment的mDetached為true時才會把fagment加入到mFragmentStore中索昂,那么mDetached又是什么呢建车?mDetached定義在Fragment中

// Set to true when the app has requested that this fragment be deactivated.

boolean mDetached;

當fragment無效時定義為true,看到這里相信大家已經(jīng)理解了椒惨,當我們傳入activity的supportFragmentManager時切換頁面后由于生命周期沒有正確的管理缤至,使得fragment還是有效的,所以mDetached扔然是false康谆,最后經(jīng)過一系列的方法走到FragmentManager的attachFragment方法最終無法添加到mFragmentStore里面领斥,如果繼續(xù)跟源碼我們會發(fā)現(xiàn)ViewPager的populate方法中有這么一段代碼

final int childCount = getChildCount();

最終會導致ViewPager的child數(shù)為0,所以會出現(xiàn)一片空白的情況

當然如果我們在activity中使用的話這種方式就沒為題沃暗,在framment中使用時一定得注意需要用到FragmentManager的時候要用childFragmentManager而不是對應的activity的supportFragmentManager月洛。

下面是我畫的關于本次問題的一個簡單的流程圖,便于大家理解

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市孽锥,隨后出現(xiàn)的幾起案子嚼黔,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異土至,居然都是意外死亡灌灾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門蛤高,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事钓株。” “怎么了陌僵?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵轴合,是天一觀的道長。 經(jīng)常有香客問我碗短,道長受葛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮总滩,結(jié)果婚禮上纲堵,老公的妹妹穿的比我還像新娘。我一直安慰自己闰渔,他們只是感情好席函,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冈涧,像睡著了一般茂附。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上督弓,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天营曼,我揣著相機與錄音,去河邊找鬼咽筋。 笑死溶推,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的奸攻。 我是一名探鬼主播蒜危,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼睹耐!你這毒婦竟也來了辐赞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤硝训,失蹤者是張志新(化名)和其女友劉穎响委,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窖梁,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡赘风,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纵刘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邀窃。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖假哎,靈堂內(nèi)的尸體忽然破棺而出瞬捕,到底是詐尸還是另有隱情,我是刑警寧澤舵抹,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布肪虎,位于F島的核電站,受9級特大地震影響惧蛹,放射性物質(zhì)發(fā)生泄漏扇救。R本人自食惡果不足惜刑枝,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望迅腔。 院中可真熱鬧仅讽,春花似錦、人聲如沸钾挟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掺出。三九已至,卻和暖如春苫费,著一層夾襖步出監(jiān)牢的瞬間汤锨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工百框, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留闲礼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓铐维,卻偏偏與公主長得像柬泽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嫁蛇,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

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