Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的很的出彩。我们先引入一个简单经常使用的Fragment事务管理代码片段:

            FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
ft.add(R.id.fragmentContainer, fragment, "tag");
ft.addToBackStack("<span style="font-family: Arial, Helvetica, sans-serif;">tag</span><span style="font-family: Arial, Helvetica, sans-serif;">");</span>
ft.commitAllowingStateLoss();

这段代码运行过后,就能够往fragmentContainer控件中增加Fragment的内部持有控件。

上一讲我们说到Fragment通过状态机的变更来生成内部的mView。当你使用的是非from layout.xml方式的时候,它会在Fragment.CREATED状态下搜索container相应的控件然后将mView增加到这个父控件中。

那么这个事务又在这里面承担什么样的作用呢?

我们先来看Manager.beginTransaction这种方法的返回值:

/**
* Start a series of edit operations on the Fragments associated with this
* FragmentManager.
*
* <p>
* Note: A fragment transaction can only be created/committed prior to an
* activity saving its state. If you try to commit a transaction after
* {@link FragmentActivity#onSaveInstanceState
* FragmentActivity.onSaveInstanceState()} (and prior to a following
* {@link FragmentActivity#onStart FragmentActivity.onStart} or
* {@link FragmentActivity#onResume FragmentActivity.onResume()}, you will
* get an error. This is because the framework takes care of saving your
* current fragments in the state, and if changes are made after the state
* is saved then they will be lost.
* </p>
*/
public abstract FragmentTransaction beginTransaction();

在Fragment的管理中FragmentManager的实现类是FragmentManagerImpl,当然这也是Android一贯的命名方式;

FragmentManagerImpl.java:
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}

FragmentManager通过返回一个叫做BackStackRecord的对象完毕事务处理。抛开Android本身,我们对于事务的理解主要源于数据库,也就是一种批量性的操作,记录下你的操作集合,然后一次性处理,保证事务处理时候的安全性和高效性。FragmentManager看待事务的观点也基本一致,BackStackRecord的核心方法是addOp(Op):

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++;
}

我们看到,对于BackStackRecord对操作处理的组织是採用"迭代器"的模式,每个操作被记录成为Op对象,又能够当作"备忘录"模式来看待。而对于加入操作的入口:

public FragmentTransaction add(Fragment fragment, String tag) {
doAddOp(0, fragment, tag, OP_ADD);
return this;
} public FragmentTransaction add(int containerViewId, Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
} public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}

是採用"Builder"的方式来组织。

文章開始我已经提到了,Fragment的事务管理是比較出彩的代码,单纯的事务採用了至少三套模式来组织,并且组织起来丝毫没有感觉。

当然Fragment带给我们的惊喜还不仅限于此。我们总上面的代码片段能够看出,实际上,通过事务类BackStackRecord生成Op对象实际上在复制BackStackRecord的属性,所以当我们分析每个Op里面的数据的时候,能够直接用BackStackRecord中的属性映射。

    int mNumOp;//Op数量
int mEnterAnim;//进入动画
int mExitAnim;//退出动画
int mPopEnterAnim;//弹出进入动画
int mPopExitAnim;//弹出退出动画
int mTransition;//转场动画
int mTransitionStyle;
boolean mAddToBackStack;//是否增加到BackStack中

Op本身属于Command模式,它的Command列表各自是:

    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的属性就是作为Command的操作数。在BackStackRecord进行Commit了之后,BackStackRecord会将自己纳入FragmentManagerImpl的命令队列中处理。

每个处理单元用于处理各自的Op操作。

我们来看下代码:

BackStackRecord.java:
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;
}

BackStackRecord在提交的时候会将自己提交到mManager的Action队列中去。

而这样的Action队列的处理能够在随意线程中进行

FragmentManager.java:

public void enqueueAction(Runnable action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (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);
}
}
}

我们看到实际上Action是被mExecCommit命令所运行,而它,将回调你所提交的BackStackRecord的run方法。

BackStackRecord.java:
Op op = mHead;
while (op != null) {
...
}

我们看到,命令是以迭代器的方式在循环执行。

不同的命令将对Fragment有不同的状态变更操作,举个简单的样例:

Op.java:

case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = op.enterAnim;
mManager.addFragment(f, false);
} break;

当我们须要Add一个Fragment的时候,Op将调用FragmentManager的addFragment方法

FragmentManager.java:
public void addFragment(Fragment fragment, boolean moveToStateNow) {
if (mAdded == null) {
mAdded = new ArrayList<Fragment>();
}
makeActive(fragment);
if (!fragment.mDetached) {
if (mAdded.contains(fragment)) {
throw new IllegalStateException("Fragment already added: "
+ fragment);
}
mAdded.add(fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
if (moveToStateNow) {
moveToState(fragment);
}
}
}

FragmentManager会将它先增加到自己的mAdded队列中去,然后通过调用moveToState方法来改变Fragment状态保证状态上的一致性。而这一部分,就是我们上一部分讲的内容,我们不再赘述。这样Fragment通过这样的优雅的方式就实现了事务的处理。

下一篇,我将给大家讲述Fragment关于Stack管理的部分源代码。

[Android]Fragment源代码分析(三) 事务的更多相关文章

  1. [Android]Fragment源代码分析(二) 状态

    我们上一讲,抛出来一个问题,就是当Activity的onCreateView的时候,是怎样构造Fragment中的View參数.要回答这个问题我们先要了解Fragment的状态,这是Fragment管 ...

  2. Android HandlerThread 源代码分析

    HandlerThread 简单介绍: 我们知道Thread线程是一次性消费品,当Thread线程运行完一个耗时的任务之后.线程就会被自己主动销毁了.假设此时我又有一 个耗时任务须要运行,我们不得不又 ...

  3. Android KLog源代码分析

    Android KLog源代码分析 Android KLog源代码分析 代码结构 详细分析 BaseLog FileLog JsonLog XmlLog 核心文件KLogjava分析 遇到的问题 一直 ...

  4. Appium Android Bootstrap源代码分析之启动执行

    通过前面的两篇文章<Appium Android Bootstrap源代码分析之控件AndroidElement>和<Appium Android Bootstrap源代码分析之命令 ...

  5. Android 消息处理源代码分析(1)

    Android 消息处理源代码分析(1) 在Android中,通常被使用的消息队列的代码在文件夹\sources\android-22\android\os下,涉及到下面几个类文件 Handler.j ...

  6. Android HttpURLConnection源代码分析

    Android HttpURLConnection源代码分析 之前写过HttpURLConnection与HttpClient的差别及选择.后来又分析了Volley的源代码. 近期又遇到了问题,想在V ...

  7. android开发源代码分析--多个activity调用多个jni库的方法

    android开发源代码分析--多个activity调用多个jni库的方法 有时候,我们在开发android项目时会遇到须要调用多个native c/jni库文件,下面是本人以前实现过的方法,假设有知 ...

  8. Android 消息处理源代码分析(2)

    Android 消息处理源代码分析(1)点击打开链接 继续接着分析剩下的类文件 Looper.java public final class Looper { final MessageQueue m ...

  9. Nouveau源代码分析(三):NVIDIA设备初始化之nouveau_drm_probe

    Nouveau源代码分析(三) 向DRM注冊了Nouveau驱动之后,内核中的PCI模块就会扫描全部没有相应驱动的设备,然后和nouveau_drm_pci_table对比. 对于匹配的设备,PCI模 ...

随机推荐

  1. 【POJ1845】Sumdiv(数论/约数和定理/等比数列二分求和)

    题目: POJ1845 分析: 首先用线性筛把\(A\)分解质因数,得到: \[A=p_1^{a_1}*p_2^{a_2}...*p_n^{a_n} (p_i是质数且a_i>0) \] 则显然\ ...

  2. 多重背包(MultPack = ZeroOnePack + CompletePack)

    HiHoCoder_offer6_04 题目4 : 奖券兑换 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi在游乐园中获得了M张奖券,这些奖券可以用来兑换奖品. ...

  3. 【转】Linux命令学习手册-split命令

    转自:http://blog.chinaunix.net/uid-9525959-id-3054325.html split [OPTION] [INPUT [PREFIX]] [功能]将文件分割成多 ...

  4. phpstorm如何在同一个文件夹打开多个目录

    phpstorm默认一个窗口只显示一个项目,如果新建一个项目,他会给你个选项卡,问你是在新窗口打开新项目还是在本窗口打开. 能不能在一个窗口打开多个项目呢?就像sublime text那样,其实是可以 ...

  5. Android互动设计-蓝牙遥控自走车iTank

    一.让Android与外部的设备互动 iTank智能型移动平台基本款简介 iTank智能型移动平台是一台履带车,车体上方的控制板有一颗微处理器,我们可以通过它的UART或是I2C接口下达指令来控制iT ...

  6. VMWare 安装Ubuntu 16.04

    1.新建虚拟机 (1)点击文件-->新建虚拟机 (2)选择 自定义(高级)--> 下一步 (3)选择Workstation 12.0 --> 下一步 (4)选择 稍后安装操作系统 - ...

  7. JS高级——扩展内置对象的方法

    基本概念 内置对象有很多,几个比较重要的:Math.String.Date.Array 基本使用 1.内置对象创建出来的对象使用的方法使用的其实都是内置对象的原型对象中的方法 (1)a并没有charA ...

  8. JS——缓慢动画封装案例

    手风琴 1.排他思想 2.ul宽度需要大一点,防止li撑开跑下去 3.一个变大其他所有变小,变小不能太小,不然会出现空白 <!DOCTYPE html> <html lang=&qu ...

  9. TensorFlow: Could not load requested Qt binding.

    使用Eclipse 引入tensorflow,出现 Could not load requested Qt binding.  问题 ImportError: Could not load reque ...

  10. How an SSL connection is established

    An SSL connection between a client and server is set up by a handshake, the goals of which are: To s ...