源代码分析Fragmentd的BackStack管理过程
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 transaction
Fragment 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 stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null); // Commit the transaction
transaction.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<BackStackRecord>();
}
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<Runnable>();
}
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<numActions; i++) {
mTmpActions[i].run();
mTmpActions[i] = null;
}
mExecutingActions = false;
didSomething = true;
} if (mHavePendingDeferredStart) {
boolean loadersRunning = false;
for (int i=0; i<mActive.size(); i++) {
Fragment f = mActive.get(i);
if (f != null && f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
}
if (!loadersRunning) {
mHavePendingDeferredStart = false;
startPendingDeferredFragments();
}
}
return didSomething;
}
因为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.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
if (FragmentManagerImpl.DEBUG) Log.v(TAG,
"OP_REPLACE: adding=" + f + " old=" + old);
//假设发现两个mContainerId一样,则进行特殊处理
if (f == null || old.mContainerId == f.mContainerId) {
if (old == f) {
//两个Fragment一样,则置空,保留old中的Fragment
op.fragment = f = null;
} else {
// 将old fragment加入到 op.removed数组中。保留op中的Fragment
if (op.removed == null) {
op.removed = new ArrayList<Fragment>();
}
op.removed.add(old);
old.mNextAnim = op.exitAnim;
if (mAddToBackStack) {
//设置old Fragment在BackStack中的Number
old.mBackStackNesting += 1;
if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+ old + " to " + old.mBackStackNesting);
}
//对old Fragment设置对应的状态属性。如mAdded、mRemoving。 从FragmentManager中移除oldFrgment的相关属性
mManager.removeFragment(old, mTransition, mTransitionStyle);
}
}
}
}
//将Fragment加入到FragmentManager中
if (f != null) {
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
}
} break;
case OP_REMOVE:
......
} op = op.next;
}
//设置Fragment的当前状态,然后依据当前状态来回调Fragment的生命周期中的相关函数。此函数控制了Fragment的生命周期和Fragment的绘制,想要彻底理解Fragment的生命周期的同学能够认真研究此函数。 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<BackStackRecord>();
}
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的时候被调用。下次我们将深入这个函数。
源代码分析Fragmentd的BackStack管理过程的更多相关文章
- android-plugmgr源代码分析
android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...
- Twitter Storm源代码分析之ZooKeeper中的目录结构
徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...
- 转:SDL2源代码分析
1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...
- 转:RTMPDump源代码分析
0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...
- 转:ffdshow 源代码分析
ffdshow神奇的功能:视频播放时显示运动矢量和QP FFDShow可以称得上是全能的解码.编码器.最初FFDShow只是mpeg视频解码器,不过现在他能做到的远不止于此.它能够解码的视频格式已经远 ...
- UiAutomator源代码分析之UiAutomatorBridge框架
上一篇文章<UIAutomator源代码分析之启动和执行>我们描写叙述了uitautomator从命令行执行到载入測试用例执行測试的整个流程.过程中我们也描写叙述了UiAutomatorB ...
- MyBatis架构设计及源代码分析系列(一):MyBatis架构
如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...
- hostapd源代码分析(三):管理帧的收发和处理
hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...
- hostapd源代码分析(二):hostapd的工作机制
[转]hostapd源代码分析(二):hostapd的工作机制 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004433 在我的上一 ...
随机推荐
- STL之priority_queue3
描述 使用STL中的优先队列,将n个点按照横坐标从小到大顺序排序,如果横坐标相同,按照纵坐标从小到大排序. 部分代码已经给出,请补充完整,提交时请勿包含已经给出的代码. int main() { in ...
- 【R笔记】R语言中的字符串处理函数
内容概览 尽管R是一门以数值向量和矩阵为核心的统计语言,但字符串同样极为重要.从医疗研究数据里的出生日期到文本挖掘的应用,字符串数据在R程序中使用的频率非常高.R语言提供了很多字符串操作函数,本文仅简 ...
- [转]从此爱上iOS Autolayout
原文地址 这篇不是autolayout教程,只是autolayout动员文章和经验之谈,在本文第五节友情链接和推荐中,我将附上足够大家熟练使用autolayout的教程.这篇文章两个月前就想写下来,但 ...
- ACM--输入三个字符(可以重复)后,按各字符的ASCII码从小到大的顺序输出这三个字符。
代码如下: #include <stdio.h> main() { char a,b,c,d; int i; scanf("%d",&i); getchar() ...
- JNI之Hello World!
基本流程: 1. 创建一个类(HelloWorld.java)2. 使用 javac 编译该类3. 利用 javah -jni 产生头文件4. 用本地代码实现头文件中定义的方法5. Run 备注:在一 ...
- [测试技术分享]DNS域传送漏洞测试
DNS域传送漏洞测试 1.简介: DNS(Domain Name System)也叫域名管理系统,它它建立在一个分布式数据库基础之上,在这个数据库里,保存了IP地址和域名的相互映射关系.正因为DNS的 ...
- npm使用常见问题及注意事项
1.npm.cnpm.yarn不要混用,一个项目只使用一个 2.NPM problem: npm ERR! extraneous 表明安装了很多不需要的三方包 使用命令 npm prune删除无用的包 ...
- 【C语言疯狂讲义】(七)C语言进制转换
1.计算机中的进制 2进制:逢二进1 0 1 8进制:逢八进1 0 1 2 3 4 5 6 7 10进制:逢十进1 默认的进制 0 - 9 16进制:逢十六进 ...
- 【HTTPS双向加密认证】
HTTPS单向认证和双向认证 nearzk-osc 发布时间: 2015/07/30 15:27 阅读: 4177 收藏: 178 点赞: 6 评论: 3 一.背景&概念 HTTPS:在htt ...
- Docker解析及轻量级PaaS平台演练(一)--Docker简介与安装
Container技术: 传统的虚拟化技术: 通过对硬件层模拟,从而实现了能够在一套硬件上面运行多个操作系统,因为通过硬件虚拟化,使得操作系统认为在它之下就是硬件层 但是实际情况是这样的:虚拟机中的O ...