Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> [Android]Fragment源碼分析(肆) Fragment棧管理

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

編輯:關於Android編程

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中存在,那麼就返回暫存在這個對象中的索引值。







  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved