博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
源代码分析Fragmentd的BackStack管理过程
阅读量:7244 次
发布时间:2019-06-29

本文共 10223 字,大约阅读时间需要 34 分钟。

1. Fragment基本使用方法
为了管理Activity中的fragments。须要调用Activity中的getFragmentManager()方法。由于FragmentManager的API是在Android 3.0。也即API level 11開始引入的,所以对于之前的版本号,须要使用support library v4中的FragmentActivity,而且使用getSupportFragmentManager()方法。

用FragmentManager能够做的工作有:
得到Activity中存在的fragment:
  使用findFragmentById()或findFragmentByTag()方法。
  将fragment弹出back stack:
popBackStack():
    将back stack中最后一次的fragment转换弹出。假设没有能够出栈的东西,返回false。
 这个函数是异步的:它将弹出栈的请求增加队列。可是这个动作直到应用回到事件循环才会运行。
为back stack加上监听器:
  addOnBackStackChangedListener()
使用Fragment时,能够运行一些动作,比方添加、移除、替换等。全部这些改变构成一个集合。这个集合被叫做一个transaction。

能够调用FragmentTransaction中的方法来处理这个transaction.
以这样得到FragmentTransaction类的实例: 

FragmentManager fragmentManager = getFragmentManager();FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

每一个transaction是一组同一时候运行的变化的集合。用add(), remove(), replace()方法,把全部须要的变化加进去,然后调用commit()方法,将这些变化应用。在commit()方法之前。你能够调用addToBackStack()。把这个transaction增加back stack中去。这个back stack是由activity管理的。当用户按返回键时。就会回到上一个fragment的状态。

以下的代码很典型,用一个新的fragment代替之前的fragment。而且将之前的状态存储在back stack中。

// Create new fragment and transactionFragment newFragment = new ExampleFragment();FragmentTransaction transaction = getFragmentManager().beginTransaction();// Replace whatever is in the fragment_container view with this fragment,// and add the transaction to the back stacktransaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);// Commit the transactiontransaction.commit();
通过调用addToBackStack(),commit()的一系列转换作为一个transaction被存储在back stack中。用户按Back键能够返回上一个转换前的状态。
调用commit()方法并不能马上运行transaction中包括的改变动作,commit()方法把transaction增加activity的UI线程队列中。

以下我们对上述代码中出现的函数进行分析。以此来逐步学习Fragment的管理机制。

getSupportFragmentManager():

public FragmentManager getSupportFragmentManager() {    return mFragments;}
该函数返回类型是FragmentManager。FragmentManager是一个抽象类,事实上现类是FragmentManager.FragmentManagerImpl

beginTransaction():

该函数在FragmentManagerIMpl中的源代码例如以下:

public FragmentTransaction beginTransaction() {	return new BackStackRecord(this);}
返回一个BackStackRecord对象,该对象是FragmentTranscation的一个子类。
BackStackRecord的声明例如以下:

final class BackStackRecord extends FragmentTransaction implements        FragmentManager.BackStackEntry, Runnable {...}
该类实现了一个重要的接口:FragmentManager.BackStackEntry, 该接口代表了fragment back stack的一个入口。能够用FragmentManager.getBackStackEntry()来检索BackStackEntry。   
接下来运行transaction.replace(), 查看BackStackRecord,调用过程源代码例如以下:

public FragmentTransaction replace(int containerViewId, Fragment fragment) {	return replace(containerViewId, fragment, null);}public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {	if (containerViewId == 0) {		throw new IllegalArgumentException("Must use non-zero containerViewId");	}	doAddOp(containerViewId, fragment, tag, OP_REPLACE);	return this;}
我们发现。replace()终于调用的函数为doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd), 将Fragment和对Fragment所进行的操作放到op链表中:

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {	fragment.mFragmentManager = mManager;	if (tag != null) {		if (fragment.mTag != null && !tag.equals(fragment.mTag)) {			throw new IllegalStateException("Can't change tag of fragment "					+ fragment + ": was " + fragment.mTag					+ " now " + tag);		}		fragment.mTag = tag;	}	if (containerViewId != 0) {		if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {			throw new IllegalStateException("Can't change container ID of fragment "					+ fragment + ": was " + fragment.mFragmentId					+ " now " + containerViewId);		}		fragment.mContainerId = fragment.mFragmentId = containerViewId;	}	Op op = new Op();	op.cmd = opcmd;	op.fragment = fragment;	addOp(op);}
该函数首先设置fragment的mFragmentManager属性。然后再设置其mContainerId和mFragmentId,最后创建Op对象,然设置对应自段。当中cmd自己主动用来标识事务的类型。分为例如以下几类:
    static final int OP_NULL = 0;
    static final int OP_ADD = 1;
    static final int OP_REPLACE = 2;
    static final int OP_REMOVE = 3;
    static final int OP_HIDE = 4;
    static final int OP_SHOW = 5;
    static final int OP_DETACH = 6;
    static final int OP_ATTACH = 7;
每一个字段的意思可直接通过英文名称获知。

Op()类是BackStackRecord中声明的结构体,本质上是一个双向链表的Node。addOp()例如以下:

void addOp(Op op) {	if (mHead == null) {		mHead = mTail = op;	} else {		op.prev = mTail;		mTail.next = op;		mTail = op;	}	op.enterAnim = mEnterAnim;	op.exitAnim = mExitAnim;	op.popEnterAnim = mPopEnterAnim;	op.popExitAnim = mPopExitAnim;	mNumOp++;}
该函数将Op对象加入到链表的末尾。并将mNumOp的值增一。

transaction.addToBackStack(null)设置了mAddToBackStack为true,源代码例如以下:

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;}
此函数将mAddToBackStack自段设置为true,并设置mName字段。
最后调用transaction.commit()来运行transaction。commit()的调用过程代码例如以下:

public int commit() {	return commitInternal(false);}int commitInternal(boolean allowStateLoss) {	if (mCommitted) throw new IllegalStateException("commit already called");	if (FragmentManagerImpl.DEBUG) {		Log.v(TAG, "Commit: " + this);		LogWriter logw = new LogWriter(TAG);		PrintWriter pw = new PrintWriter(logw);		dump("  ", null, pw, null);	}	mCommitted = true;	if (mAddToBackStack) {		mIndex = mManager.allocBackStackIndex(this);	} else {		mIndex = -1;	}	mManager.enqueueAction(this, allowStateLoss);	return mIndex;}
因为mAddToBackStack为true,所以会用FragmentManager为BackstackRecorder也即FragmentTransaction分配一个index,分配步骤例如以下:

public int allocBackStackIndex(BackStackRecord bse) {	synchronized (this) {		if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {			if (mBackStackIndices == null) {				mBackStackIndices = new ArrayList
(); } int index = mBackStackIndices.size(); if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse); mBackStackIndices.add(bse); return index; } else { int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1); if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse); mBackStackIndices.set(index, bse); return index; } }}
FragmentManager用mAvailBackStackIndices和mBackStackIndices两个数组来为BackStackRecord分配Index。mAvailBackStackIndices用来存储在mBackStackIndices中可以分配的Index,mBackStackIndices则用来保存BackStackRecord。

这利用两个数组可以降低对mBackStackIndices的动态分配大小的次数,是一个以空间换时间的策略。上面的代码首先推断是否有可用的Index分配给BackStackRecord,若无则直接将BackStackRecord插入到mBackStackIndices。若存在的话则从mAvailBackStackIndices的队尾取出一个index。然后设置mBackStackIndices中该index下的值。

让我们回到commit()中,该函数最后运行mManager.enqueAction(),源代码例如以下:

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
(); } mPendingActions.add(action); if (mPendingActions.size() == 1) { mActivity.mHandler.removeCallbacks(mExecCommit); mActivity.mHandler.post(mExecCommit); } }}
该函数首先进行状态监測,查看该Fagment所在的Activity的生命周期是否处于Saving Activity之前,由于Activity保存状态往往是由用户离开那个Activity所造成的,在此之后运行commit会丢失一些状态信息。

针对这样的情况,能够使用commitAllowingStateLoss().最后将BackStackRecord加入到运行队列中。当第一次往运行 队列中加入消息时,首先会从消息队列中全部callback属性为mExecCommit的消息删除,然后又一次将mExecCommit加入到消息队列。mExecCommit的定义例如以下:

Runnable mExecCommit = new Runnable() {	@Override	public void run() {		execPendingActions();	}};
execPendingActions()仅仅能在主线程内被调用,其内部通过一个循环对mPendingActions中的Actions进行运行。值得注意的是,每运行一次循环,mPendingActions中的全部Action都会被加入到一个暂时数组中。然后这个数组被变量一遍以运行数组中的每一个Runnable。同一时候。每一个Runnable直接被调用了run,而不是开个线程运行的。当这个Runnable在运行的时候。mPendingActions数组可能会被加入内容。

当某一时刻mPendingActions中的内容为空,则while循环退出。此部分代码例如以下:

public boolean execPendingActions() {    if (mExecutingActions) {        throw new IllegalStateException("Recursive entry to executePendingTransactions");    }        if (Looper.myLooper() != mActivity.mHandler.getLooper()) {        throw new IllegalStateException("Must be called from main thread of process");    }    boolean didSomething = false;    while (true) {        int numActions;                synchronized (this) {            if (mPendingActions == null || mPendingActions.size() == 0) {                break;            }                        numActions = mPendingActions.size();            if (mTmpActions == null || mTmpActions.length < numActions) {                mTmpActions = new Runnable[numActions];            }            mPendingActions.toArray(mTmpActions);            mPendingActions.clear();            mActivity.mHandler.removeCallbacks(mExecCommit);        }        //一次性运行完数组中全部的Action        mExecutingActions = true;        for (int i=0; i
因为BackstackRecorder实现了Runnable,我们来看看BackStackRecorder中的run(),例如以下所看到的:

public void run() {	if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);	if (mAddToBackStack) {		if (mIndex < 0) {			throw new IllegalStateException("addToBackStack() called after commit()");		}	}	bumpBackStackNesting(1);	Op op = mHead;	//遍历op,依据cmd的类型对Fragment和FragmentManager进行对应的设置	while (op != null) {		switch (op.cmd) {			case OP_ADD: {				Fragment f = op.fragment;				f.mNextAnim = op.enterAnim;				//将Fragment加入到FragmentManager中,其源代码显示是将Fragment加入到FragmentManager中的mActive数组中,并将Fragment加入到了数组mAdded中。				mManager.addFragment(f, false);			} break;			case OP_REPLACE: {				Fragment f = op.fragment;				if (mManager.mAdded != null) {					//遍历已经加入的Fragment。					for (int i=0; i

mManager.moveToState(mManager.mCurState, mTransition, mTransitionStyle, true); //将BackStackRecord加入到BackStack中。并回调onBackStackChanged if (mAddToBackStack) { mManager.addBackStackState(this); } }

addBackStackState()的源代码例如以下:

void addBackStackState(BackStackRecord state) {	if (mBackStack == null) {		mBackStack = new ArrayList
(); } mBackStack.add(state); //回调onBackStackChanged reportBackStackChanged();}
能够看到传说中的BackStack就是在这里被创建的, FragmentManager中的BackStack主要是用来存储FragmentTransaction的。

小结:

FragmentTransaction中的Op链用来保存add、remove、replace等action,在FragmentTransaction的run运行时。Op链会被变量以调整每一个节点的内容。

FragmentManager使用一个BackStack来管理FragmentTransaction。使用mAdded数组来加入被add的Fragment,Fragment的创建、显示等行为都受FragmentManager的控制。

FragmentManager中的moveToState()是一个很重要的函数。在FragmentTransaction run的时候被调用。下次我们将深入这个函数。

转载地址:http://gtybm.baihongyu.com/

你可能感兴趣的文章
如何javascript获取css中的样式
查看>>
mysql INFORMATION_SCHEMA (转)
查看>>
多线程之异步编程: 经典和最新的异步编程模型,async与await
查看>>
length
查看>>
JDK源码阅读--HashMap
查看>>
Adroid 展开收起效果实现
查看>>
PHP:第五章——字符串转换与比较
查看>>
0阶 无符号指数哥伦布编码
查看>>
18、配置嵌入式servlet容器(2)
查看>>
URL重写
查看>>
移植spdylay到libcurl
查看>>
Codeforces Round #447 (Div. 2) C. Marco and GCD Sequence【构造/GCD】
查看>>
求多个区间合并后区间大小的巧妙解决方法【差分】
查看>>
转载:AAC编解码概述
查看>>
POJ 3370 Halloween treats( 鸽巢原理简单题 )
查看>>
STL vector list deque区别与实现(总结)
查看>>
讨论76 怎么查一下我机器的内存?AIX环境
查看>>
001设计模式 -- 策略模式
查看>>
Java中的基本数据类型
查看>>
wordpress 插件推荐
查看>>