編輯:關於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();
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 ArrayListstates = 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中存在,那麼就返回暫存在這個對象中的索引值。
MIUI 8公測之後,MIUI官方微博隔一段時間就會將米粉集中反饋的問題以長微博的形式進行解答,同時爆料一些MIUI的新功能。繼上次透露MIUI 8將新增手
您可下載源碼,運行看效果:點擊打開鏈接 一)切換為英文的代碼: Locale.setDefault(Locale.ENGLISH); Configuration c
1.強制使用http替換https鏈接 Tools》選擇Options,勾選上”Use Download Cache”和”Force
需求分析:很多時候,我們需要在視圖中顯示不同樣式的文字,但是為了減少viewgroup層級,不想新增很多個TextView控件來實現不同樣式的文字。那麼有沒有一種方式能夠