The fragment stack is part of the fragment management, and the essential difference between it and the activity stack is that apart from the differences in data structures and logic, the main differences are:
1.Fragment management is within the process space
2.Fragment management is normally done under a window.
Fragment management is better understood in a process space because we know that activity management is relatively complex, its management is through IPC calls, one end of the IPC is our client, and the server is the AMS service. Activity management is based on window, and fragment management is generally based on the view under the same window. In my opinion, fragment management is undoubtedly the Gospel of Android, because it is more lightweight and relatively faster. And this fragment registration can not be implemented by registering Androidmanifest.xml, which means that you can implement a very good plug-in system.
Perhaps you crossing still do not understand, why the son of Brother Mo in the opening so bold, that is because the son of the ink hope that everyone as far as possible to the code structure to fragment management rely on. Of course, I still habitually remind you, fragment is not a view, not a control, do not look at it with view, it is a container, lighter than the activity of the container.
We return to the topic of this chapter, fragment stack management, perhaps you can not quite intuitively understand what is the fragment stack, we introduce a piece of code:
Fragmenttransaction ft = This.getsupportfragmentmanager (). BeginTransaction (); fragment = new TestFragment1 (); Ft.add (R.id.fragmentcontainer, Fragment, "test"); Ft.settransition (Fragmenttransaction.transit_fragment_open); <span style= "color: #ff0000;" ><strong>ft.addtobackstack ("test");</strong></span> Ft.commitallowingstateloss ();
In the previous chapter, we wrote about fragment's transaction management, and we know that the transaction we commit will be executed in the next UI thread message, and I've annotated a code addtobackstack in red. In fact, for the name parameter inside we are dispensable. It's just an identification symbol when we're at dump. We now make a breakpoint on this method, and we know that when we call this method the direct result is that it will return to our last transaction when we press back. We know that the back button is handled in the activity's onbackpressed callback.
<pre name= "code" class= "java" >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 will call the executependingtransactions in Popstack, as we said in the previous chapter, the transaction-based fragment model will exist in the queue, And this method is to execute all the transactions in the queue again. Fragment's transaction management is a way of using memos, so all your operations are recorded in its own data structure, and every data operation is reversible. This is one of the two points of fragment, that is, when you do the add operation, there must be a remove operation corresponding to it, this corresponding operation is recorded in the Backstackrecord Popfrombackstack method. Let's look at this part of the logic first:
Android.support.v4.app.BackStackRecord:public void Popfrombackstack (Boolean dostatemove) {... switch (op.cmd) { <span style= "color: #ff0000;" >case Op_add</span>: { Fragment f = op.fragment; F.mnextanim = Op.popexitanim; <span style= "color: #cc0000;" >mManager.removeFragment</span> (F, Fragmentmanagerimpl.reversetransit (mtransition), Mtransitionstyle);} }
I see that, in fact, in the Popfrombackstack, Backstackrecord's own record is reversed, which is why it can be used to fragment management in reverse mode when you roll back the fragment stack. We'll talk about Fragmentmanager later. In order to achieve fragment fallback, we first want to record the entire fragment call flow, as well as callback fragment corresponding Backstackrecord pop method. One of the entrances to the fragment call is in the Boolean popbackstackstate (Handler Handler, String name, int ID, int flags):
Boolean popbackstackstate (Handler Handler, String name, int ID, int flags) {if (Mbackstack = = null) {R Eturn false; if (name = = null && ID < 0 && (Flags & pop_back_stack_inclusive) = = 0) {<spa n style= "color: #3366ff;" > Int last = mbackstack.size ()-1; if (last < 0) {return false; } final Backstackrecord BSS = Mbackstack.remove (last); Bss.popfrombackstack (TRUE); Reportbackstackchanged ();</span>} else {<span style= "color: #ff0000;" >int index =-1; if (name! = NULL | | ID >= 0) {//If a name or ID is specified. 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<backstackrecord> states = new arraylist<backstackrecord> (); 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 ();</span>} return true; }
As we can see, the actual data structure that fragment manages fragment storage is: Mbackstack object. Its type is a strongly-typed ArrayList. It is not difficult to guess that it is a linear table that simulates a stack data structure. The blue part of the code is better understood, directly to get the last state, and then by callback its pop method to end fragment to their own management. Some people may be confused, fragment has a record in the Fragmentmanager, why create more than one Backstackrecord object to record it? In fact, this problem is similar to the management of activity, the most intuitive answer I can give you is the focus is different, the focus of Fragmentmanager is to manage the state of fragment, The purpose of Backstackrecord is to record the operation of fragment. To make it easy for everyone to understand the logic of the red part, I first introduced a piece of code:
if (v = = view1) { fragmenttransaction ft = This.getsupportfragmentmanager (). BeginTransaction (); fragment = new TestFragment1 (); Ft.add (R.id.fragmentcontainer, Fragment, "test"); Ft.settransition (Fragmenttransaction.transit_fragment_open); <strong><span style= "color: #ff0000;" >ft.addtobackstack ("Test" +index);</span></strong> ft.commitallowingstateloss (); Index + +; } else { This.getsupportfragmentmanager (). Popbackstack ("Test2", fragmentmanager.pop_back_stack_inclusive); }
When you add to the backstack inside of the 10 fragment, pop to the test2 position of the fragment, it will backstack the test2 after the record is clear, yes, Is the activity's cleartop or activity's startup parameter setting. Of course, the intent flag and startup mode of activity is a thing in itself, just a wrapper. We pass the phenomenon in the back of the code is very good understanding, it is simply to get the corresponding Backstackrecord, and then recorded in a list, and then the batch of elimination.
Well, the article is written here, I believe you have a basic understanding of fragment stack management, but we still do not involve fragment how to join the stack problem. We callback Backstackrecord's Addtostack method:
Public fragmenttransaction Addtobackstack (String name) { if (!mallowaddtobackstack) { throw new IllegalStateException ("This fragmenttransaction was not a allowed to being added to the back stack.") ; Maddtobackstack = true; Mname = name; return this; }
Here, Backstackrecord is set to True for Maddtobackstack. An index number is assigned at commit:
int Commitinternal (Boolean allowstateloss) { if (mcommitted) throw new IllegalStateException ("Commit already Called "); Mcommitted = true; if (maddtobackstack) { Mindex = Mmanager.<strong><span style= "color: #33cc00;" >allocBackStackIndex</span></strong> (this); } else { Mindex =-1; } Mmanager.enqueueaction (this, allowstateloss); return mindex; }
In fact, the way the manager assigns index is very simple:
public int Allocbackstackindex (Backstackrecord BSE) { synchronized (this) { if (<strong> Mavailbackstackindices</strong> = = NULL | | mavailbackstackindices.size () <= 0) { if ( Mbackstackindices = = null) { mbackstackindices = new arraylist<backstackrecord> (); } int index = <strong>mbackstackindices</strong>.size (); Mbackstackindices.add (BSE); return index; } else { int index = mavailbackstackindices . Remove (Mavailbackstackindices.size ()-1); Mbackstackindices.set (index, BSE); return index;}} }
Here are the main two variables mavailbackstackindices and mbackstackindices. In fact, we can easily understand these two variables, when we pop out of fragment, it will put its index in the mavailbackstackindices queue, When we need to apply for an index, if it exists in the mavailbackstackindices, then it returns the index value in the object that is temporarily present.