In the past two days, I have been studying plug-in programming and encountered some problems when using Fragment. So I checked the source code and analyzed the original code of Fragment, FragmentManager and several other APIs to see how they work.
We know that Fragment has an onCreateView() method, which is called when Fragment creates a View and returns a View object. So when is the onCreateView called? We found a method in the Fragment class, performCreateView() method.
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return null; }
When will the performCreateView method be called? The code to call it cannot be found in the Fragment. We can guess that it will probably be in the FragmentManager.
View performCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (mChildFragmentManager != null) { (); } return onCreateView(inflater, container, savedInstanceState); }
In the FragmentManager, we found the code to call. In the moveToState() method, this method is a bit big, and I only pasted part of the code. As you can see, it will be called when the Fragment is initialized or created. And we know that the created View is assigned to the mView member variable of the Fragment.
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) { switch () { case : if () { = (( ), null, ); } break; case : if (!) { = (( ), container, ); } break; } }
Next, we need to see when the moveToState() method will be called. After searching for it, I found that this method has been called in many places. This makes it difficult for us to reverse the code to find it. So let’s change our thinking and make a positive analysis. Why is it pushed correctly? Let’s see how we use Fragment and FragmentManager to analyze it.
Generally, we use the methods of getFragmentManager() or getSupportFragmentManager() to obtain the FragmentManager. Taking FragmentActivity as an example, generally, we call one of these two methods in a subclass of this class.
We found the corresponding code in the FragmentActivity. FragmentManager is an abstract class. FragmentManagerImpl is a subclass of FragmentManager. It is an internal class in the same java file as FragmentManager. It is an implementation of FragmentManager.
//FragmentManagerImpl is subclass of FragmentManager final FragmentManagerImpl mFragments = new FragmentManagerImpl(); public FragmentManager getSupportFragmentManager() { return mFragments; }
After obtaining the FragmentManager, we usually call the beginTransaction() method and return a FragmentTransaction. Let's look at the code.
public abstract FragmentTransaction beginTransaction(); FragmentManagerImpl extends FragmentManager @Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); } /** * Static library support version of the framework's {@link }. * Used to write apps that run on platforms prior to Android 3.0. When running * on Android 3.0 or above, this implementation is still used; it does not try * to switch to the framework's implementation. See the framework SDK * documentation for a class overview. */ public abstract class FragmentTransaction
We found that FragmentManager is an abstract method implemented in FragmentManagerImpl. () returns a BackStackRecord, and FragmentTransaction is an abstract class. So what the hell is BackStackRecord.
We found the BackStackRecord class. We noticed that it inherits from FragmentTransaction and implements the Runable interface. There are many methods, let’s analyze one we are more commonly used, such as the add() method.
final class BackStackRecord extends FragmentTransaction implements , Runnable final FragmentManagerImpl mManager; public BackStackRecord(FragmentManagerImpl manager) { mManager = manager; }
The add() method actually does nothing, let's follow it all the way.
public FragmentTransaction add(Fragment fragment, String tag) { doAddOp(0, fragment, tag, OP_ADD); return this; } private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { = mManager; if (tag != null) { if ( != null && !()) { throw new IllegalStateException("Can't change tag of fragment " + fragment + ": was " + + " now " + tag); } = tag; } if (containerViewId != 0) { if ( != 0 && != containerViewId) { throw new IllegalStateException("Can't change container ID of fragment " + fragment + ": was " + + " now " + containerViewId); } = = containerViewId; } Op op = new Op(); = opcmd; = fragment; addOp(op); } void addOp(Op op) { if (mHead == null) { mHead = mTail = op; } else { = mTail; = op; mTail = op; } = mEnterAnim; = mExitAnim; = mPopEnterAnim; = mPopExitAnim; mNumOp++; }
I chased me until addOp() and it was broken, as if I had done nothing. But it is probably added to a linked list in an add operation. Then what should we do? Generally, we will commit after adding, let’s see what the commit has done.
public int commit() { return commitInternal(false); } int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); mCommitted = true; if (mAddToBackStack) { mIndex = (this); } else { mIndex = -1; } (this, allowStateLoss); return mIndex; }
commit seems to have nothing special to do, but you can see this line of code (this, allowStateLoss); see the method name of enqueueAction, and you should do something.
Similarly, we found this method in FragmentManagerImpl.
public void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mActivity == null) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<Runnable>(); } (action); if (() == 1) { (mExecCommit); (mExecCommit); } } }
This method adds our BackStackRecord -- which is actually a FragmentTransaction, and Runnable -- to an ArrayList of mPendingActions. Then call (mExecCommit); What the hell is mExecCommit?
Runnable mExecCommit = new Runnable() { @Override public void run() { execPendingActions(); } }; (mExecCommit); It means it was executed in the main thread mExecCommit of run method。别问我咋知道of。 execPendingActions() method稍微比较大,I wrote the comments in the code。 public boolean execPendingActions() { if (mExecutingActions) { throw new IllegalStateException("Recursive entry to executePendingTransactions"); } //If it is not in the main thread, throw an exception. if (() != ()) { throw new IllegalStateException("Must be called from main thread of process"); } boolean didSomething = false; // There is a while true loop here. while (true) { int numActions; // Here is a synchronization statement block, transfer the elements in the last mPendingActions to the mTmpActions array. And execute the run method. Whose run method should be executed? ! It is BackStackRecord, which is FragmentTransaction. I posted the run method of BackStackRecord at the end. synchronized (this) { if (mPendingActions == null || () == 0) { break; } numActions = (); if (mTmpActions == null || < numActions) { mTmpActions = new Runnable[numActions]; } (mTmpActions); (); (mExecCommit); } mExecutingActions = true; for (int i=0; i<numActions; i++) { mTmpActions[i].run(); mTmpActions[i] = null; } mExecutingActions = false; didSomething = true; } // There are several lines of code here, I don’t know what to do. Anyway, I just made some judgments and the startPendingDeferredFragments() method may be called in the end. if (mHavePendingDeferredStart) { boolean loadersRunning = false; for (int i=0; i<(); i++) { Fragment f = (i); if (f != null && != null) { loadersRunning |= (); } } if (!loadersRunning) { mHavePendingDeferredStart = false; startPendingDeferredFragments(); } } return didSomething; }
The startPendingDeferredFragments method is another piece of code that I don't know what it means. Finally, performPendingDeferredStart() may be called
void startPendingDeferredFragments() { if (mActive == null) return; for (int i=0; i<(); i++) { Fragment f = (i); if (f != null) { performPendingDeferredStart(f); } } }
In this method, we see the familiar moveToState() method. Next is the above analysis, the onCreateView of Fragment will be called.
public void performPendingDeferredStart(Fragment f) { if () { if (mExecutingActions) { // Wait until we're done executing our pending transactions mHavePendingDeferredStart = true; return; } = false; moveToState(f, mCurState, 0, 0, false); } }
Let’s take a look at the run method of BackStackRecord. This piece of code is a bit big, so I still write comments in the code.
public void run() { if () (TAG, "Run: " + this); if (mAddToBackStack) { if (mIndex < 0) { throw new IllegalStateException("addToBackStack() called after commit()"); } } bumpBackStackNesting(1); TransitionState state = null; SparseArray<Fragment> firstOutFragments = null; SparseArray<Fragment> lastInFragments = null; if (SUPPORTS_TRANSITIONS) { firstOutFragments = new SparseArray<Fragment>(); lastInFragments = new SparseArray<Fragment>(); calculateFragments(firstOutFragments, lastInFragments); state = beginTransition(firstOutFragments, lastInFragments, false); } int transitionStyle = state != null ? 0 : mTransitionStyle; int transition = state != null ? 0 : mTransition; // Note that the while loop is about to start here, and iterate through the linked list we just mentioned. Op op = mHead; while (op != null) { int enterAnim = state != null ? 0 : ; int exitAnim = state != null ? 0 : ; switch () { // OP_ADD is very simple, (f, false); The other few are also similar, calling the corresponding method of mManager. case OP_ADD: { Fragment f = ; = enterAnim; (f, false); } break; case OP_REPLACE: { Fragment f = ; if ( != null) { for (int i=0; i<(); i++) { Fragment old = (i); if () (TAG, "OP_REPLACE: adding=" + f + " old=" + old); if (f == null || == ) { if (old == f) { = f = null; } else { if ( == null) { = new ArrayList<Fragment>(); } (old); = exitAnim; if (mAddToBackStack) { += 1; if () (TAG, "Bump nesting of " + old + " to " + ); } (old, transition, transitionStyle); } } } } if (f != null) { = enterAnim; (f, false); } } break; case OP_REMOVE: { Fragment f = ; = exitAnim; (f, transition, transitionStyle); } break; case OP_HIDE: { Fragment f = ; = exitAnim; (f, transition, transitionStyle); } break; case OP_SHOW: { Fragment f = ; = enterAnim; (f, transition, transitionStyle); } break; case OP_DETACH: { Fragment f = ; = exitAnim; (f, transition, transitionStyle); } break; case OP_ATTACH: { Fragment f = ; = enterAnim; (f, transition, transitionStyle); } break; default: { throw new IllegalArgumentException("Unknown cmd: " + ); } } op = ; } // Finally, the moveToState() method was called. The difference from the previous one is to see the last parameter, one is true and one is false. // Also note that this line of code is after the while loop. (, transition, transitionStyle, true); if (mAddToBackStack) { (this); } }
The above is the code analysis of Android Fragment and FragmentManager introduced to you by the editor. I hope it will be helpful to you. If you have any questions, please leave me a message and the editor will reply to you in time. Thank you very much for your support for my website!