[Android]Fragment源碼分析(肆) Fragment棧管理

來源:互聯網
上載者:User

[Android]Fragment源碼分析(肆) Fragment棧管理

Fragment的棧是Fragment管理頗為出彩的一部分,它跟Activity棧的本質差異除了在資料結構上和邏輯上的不同之外,主要區別還在於:

1.Fragment管理是在進程空間內的

2.Fragment的管理一般情況下是一個Window下進行的。

Fragment的管理在一個進程空間內是比較好理解的,因為我們知道Activity的管理其實相對複雜,它的管理是通過IPC調用,IPC的一端是我們的Client,而作為Server的是Ams服務。Activity的管理是基於Window的,而Fragment的管理普遍是基於同一個window下的View來實現的。在我看來,Fragment管理無疑是Android的福音,因為它更輕量級,相對更快。而且這種Fragment註冊也可以不通過註冊AndroidManifest.xml的方式來實現,意味著你可以實現一個非常好的外掛程式系統。

或許各位看官還不理解,為何子墨兄為何要在開篇如此濃墨重彩,那是因為子墨希望大家盡量的將代碼結構往Fragment管理上靠。當然我還是習慣性的提醒各位,Fragment不是View,不是控制項,不要用View的觀點去看待它,它就是一個容器,比Activity輕量級的容器。

我們回到本章的課題,Fragment的棧管理,或許你還不能很直觀的瞭解什麼是Fragment棧,我們引入一段代碼:

            FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();            fragment = new TestFragment1();            ft.add(R.id.fragmentContainer, fragment, "test");            ft.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN);            ft.addToBackStack("test");            ft.commitAllowingStateLoss();

上一章我們濃墨重彩寫了Fragment的交易管理,我們知道,我們提交的這個事務將會在下一個UI線程訊息中執行,我在裡面用紅色標註了一段代碼addToBackStack的方法。實際上,對於裡面的name參數我們可有可無。它只是我們在dump的時候的一個標識符號。我們現在這個方法上打個斷點,我們知道,當我們調用這個方法的直接結果就是在我們按Back的時候,它會返回到我們上一個事務中去。我們知道對於Back按鈕的處理是在Activity的onBackPressed回調中。

android.support.v4.app.FragmentManager:@Override    public boolean popBackStackImmediate() {        checkStateLoss();        executePendingTransactions();        return popBackStackState(mActivity.mHandler, null, -1, 0);    }

FragmentActivity:/** * Take care of popping the fragment back stack or finishing the activity * as appropriate. */ public void onBackPressed() { if (!mFragments.popBackStackImmediate()) { finish(); } }


FragmentManager在PopStack的時候會調用一遍executePendingTransactions,我們上一章說過,基於事務的Fragment模型會將事務存在在隊列中,而這個方法就是將隊列中的所有事務執行一遍。Fragment的交易管理是採用備忘錄的方式,所以你所有的操作都會記錄在它自己的資料結構中,而且每一個資料操作都是可逆的。這是Fragment的兩點之一,也就是當你進行add的操作時候,必然有一個remove操作與其對應,這種對應的操作被記錄在BackStackRecord的popFromBackStack方法中。我們先來看下這部分邏輯:

android.support.v4.app.BackStackRecord:public void popFromBackStack(boolean doStateMove) { ...switch (op.cmd) {            case OP_ADD: {                Fragment f = op.fragment;                f.mNextAnim = op.popExitAnim;                mManager.removeFragment(f,                        FragmentManagerImpl.reverseTransit(mTransition),                        mTransitionStyle);            }}

我看看到,實際上在popFromBackStack中,BackStackRecord對本身的記錄進行了逆操作,這就是為什麼在你在回退Fragment棧的時候它能用逆的方式來進行Fragment管理。我們回頭再說FragmentManager。為了實現Fragment的回退,首先我們要記錄整個Fragment的調用流程,還有回調Fragment對應的BackStackRecord的pop方法。Fragment調用的入口之一在boolean popBackStackState(Handler handler, String name, int id, int flags)中:

boolean popBackStackState(Handler handler, String name, int id, int flags) {        if (mBackStack == null) {            return false;        }        if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {            int last = mBackStack.size() - 1;            if (last < 0) {                return false;            }            final BackStackRecord bss = mBackStack.remove(last);            bss.popFromBackStack(true);            reportBackStackChanged();        } else {            int index = -1;            if (name != null || id >= 0) {                // If a name or ID is specified, look for that place in                // the stack.                index = mBackStack.size() - 1;                while (index >= 0) {                    BackStackRecord bss = mBackStack.get(index);                    if (name != null && name.equals(bss.getName())) {                        break;                    }                    if (id >= 0 && id == bss.mIndex) {                        break;                    }                    index--;                }                if (index < 0) {                    return false;                }                if ((flags & POP_BACK_STACK_INCLUSIVE) != 0) {                    index--;                    // Consume all following entries that match.                    while (index >= 0) {                        BackStackRecord bss = mBackStack.get(index);                        if ((name != null && name.equals(bss.getName()))                                || (id >= 0 && id == bss.mIndex)) {                            index--;                            continue;                        }                        break;                    }                }            }            if (index == mBackStack.size() - 1) {                return false;            }            final ArrayList states = new ArrayList();            for (int i = mBackStack.size() - 1; i > index; i--) {                states.add(mBackStack.remove(i));            }            final int LAST = states.size() - 1;            for (int i = 0; i <= LAST; i++) {                states.get(i).popFromBackStack(i == LAST);            }            reportBackStackChanged();        }        return true;    }

我們可以看出,實際上Fragment管理Fragment儲存的資料結構是:mBackStack對象。它的類型是強型別的ArrayList。我們不難猜出它是採用線性表的方式來類比Stack資料結構。藍色部分代碼比較好瞭解,直接取得最後一個狀態,然後通過回調它的pop方法來結束Fragment對自己的管理。有些人可能會帶有困惑,Fragment已經在FragmentManager中存在有記錄,為何要多建立一個BackStackRecord對象來記錄呢?實際上這個問題跟Activity的管理很相似,我能給你的最直觀的回答就是側重點不同,FragmentManager的側重點是為了管理Fragment的狀態,而BackStackRecord的目的是為了記錄Fragment的操作。為了方便大家瞭解紅色部分的邏輯我先引入一段代碼:

if (v == view1) {            FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();            fragment = new TestFragment1();            ft.add(R.id.fragmentContainer, fragment, "test");            ft.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN);            ft.addToBackStack("test"+index);            ft.commitAllowingStateLoss();            index ++;        } else {            this.getSupportFragmentManager().popBackStack("test2", FragmentManager.POP_BACK_STACK_INCLUSIVE);        }

當你add到BackStack裡面10個的Fragment的時候,pop到test2位置的fragment的時候,它會將BackStack中test2之後的記錄都clear掉,對,就是Activity的clearTop或者Activity的啟動參數設定。當然,Activity的Intent的Flag和啟動模式本身就是一種東西,只不過做了封裝而已。我們通過現象在回到代碼就非常的好理解,它無非就是取得對應的BackStackRecord,然後記錄在一個List裡面,然後進行批量的消除。

好了,文章寫到這裡,相信你對Fragment的Stack的管理有了一個基本的認識,但是我們還是沒有涉及Fragment如何加入Stack的問題。我們回調BackStackRecord的addToStack方法:

public FragmentTransaction addToBackStack(String name) {        if (!mAllowAddToBackStack) {            throw new IllegalStateException(                    "This FragmentTransaction is not allowed to be added to the back stack.");        }        mAddToBackStack = true;        mName = name;        return this;    }
這裡,BackStackRecord對mAddToBackStack被設定為true.在Commit的時候會分配一個index號碼:

int commitInternal(boolean allowStateLoss) {        if (mCommitted)            throw new IllegalStateException("commit already called");        mCommitted = true;        if (mAddToBackStack) {            mIndex = mManager.allocBackStackIndex(this);        } else {            mIndex = -1;        }        mManager.enqueueAction(this, allowStateLoss);        return mIndex;    }

實際上,對於Manager分配Index的方式非常簡單:

public int allocBackStackIndex(BackStackRecord bse) {        synchronized (this) {            if (mAvailBackStackIndices == null                    || mAvailBackStackIndices.size() <= 0) {                if (mBackStackIndices == null) {                    mBackStackIndices = new ArrayList();                }                int index = mBackStackIndices.size();                mBackStackIndices.add(bse);                return index;            } else {                int index = mAvailBackStackIndices                        .remove(mAvailBackStackIndices.size() - 1);                mBackStackIndices.set(index, bse);                return index;            }        }    }

這裡主要是兩個變數mAvailBackStackIndices和mBackStackIndices。實際上我們可以比較簡單的理解這兩個變數,當我們pop出Fragment的時候,它會將它的index存放在mAvailBackStackIndices隊列中,當我們需要申請一個index的時候如果mAvailBackStackIndices中存在,那麼就返回暫存在這個對象中的索引值。







聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.