fragment

1.FragmentTransaction的commit()和commitAllowingStateLoss()的区别

1.1源码

1.1.1 FragmentTransaction

什么是FragmentTransaction

l         所有对Fragment的操作,比如增加、移除、替换等的改变,都可构成一个集合,这个集合被叫做一个transaction,而FragmentTransaction中的方法,可以处理这个transaction;

l         并且可以将transaction存进由activity管理的back stack中,这样用户就可以进行fragment变化的回退操作;

FragmentTransaction的实例

l         FragmentManager  mFragmentManager = getSupportFragmentManager();

FragmentTransaction  mFragmentTransaction = mFragmentManager.beginTransaction();

commit

l         FragmentTransaction最后需要进行commit的操作才有效,但你只能在activity存储它的状态(当用户要离开activity时)之前调用commit(),如果在存储状态之后调用commit(),将会抛出一个异常

l         这是因为当activity再次被恢复时commit之后的状态将丢失。如果丢失也没关系,可以使用commitAllowingStateLoss()方法

 

  问题来了:这两个方法区别何在?

1.1.2查看源码搞搞报错原因

1.         找一下FragmentManager和FragmentTransaction的实例代码

public FragmentManager getSupportFragmentManager() {

        return mFragments;

}

2.         继续找mFragments

final FragmentManagerImpl mFragments = new FragmentManagerImpl();

3.         继续找FragmentManagerImpl

final class FragmentManagerImpl extends FragmentManager {

    @Override

    public FragmentTransaction beginTransaction() { //我们可以发现transaction发现实例的真相

        return new BackStackRecord(this);

    }

}

4.         继续找BackStackRecord

final class BackStackRecord extends FragmentTransaction implements

        FragmentManager.BackStackEntry, Runnable {

       ..........

       public int commit() {

        return commitInternal(false);

    }

    public int commitAllowingStateLoss() {

        return commitInternal(true);

    }

       int commitInternal(boolean allowStateLoss) {

        if (mCommitted) throw new IllegalStateException("commit already called");

        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this);

        mCommitted = true;

        if (mAddToBackStack) {

            mIndex = mManager.allocBackStackIndex(this);

        } else {

            mIndex = -1;

        }

        mManager.enqueueAction(this, allowStateLoss);

        return mIndex;

    }

       ..........

  }

在这个类里,我们可以看到commit()和commitAllowingStateLoss()的区别——他们都调用了同一个方法commitInternal(),区别就是参数allowStateLoss不同——我们看到这个参数,最终传到mManager.enqueueAction(this, allowStateLoss)

5.         那我们又去FragmentManager瞧瞧

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

方法在对 commit和commitAllowingStateLoss的传参进行判断后,将任务扔进activity的线程队列中。那这个两个方法区别就在传参判断后的处理方法checkStateLoss

6.         让我们查看一下checkStateLoss方法

看对参数进行判断后,做了什么样的处理:

private void checkStateLoss() {

        if (mStateSaved) {

            throw new IllegalStateException(

                    "Can not perform this action after onSaveInstanceState");

        }

        if (mNoTransactionsBecause != null) {

            throw new IllegalStateException(

                    "Can not perform this action inside of " + mNoTransactionsBecause);

        }

    }

可以发现:

1)        当使用commit方法时,系统将进行状态判断,如果状态(mStateSaved)已经保存,将发生"Can not perform this action after onSaveInstanceState"错误。

2)        如果mNoTransactionsBecause已经存在,将发生"Can not perform this action inside of " + mNoTransactionsBecause错误

1.2下面开始讲道理

1.2.1activity脆弱的人生

Android应用在Android运行环境里很难决定自己的命运。Android系统可以在任何时候通过结束一个进程以释放内存,而且background activities可能在没有任何警告的情况下被清理。

为了确保这种不确定的行为对于用户是透明的,在Activity可以销毁之前,通过调用onSaveInstanceState()方法,架构给每个Activity一个保存自身状态的机会。

因此在重新加载已保存的状态时,对于foreground和background Activities的切换,为用户带来了无缝切换的体验。用户不用去关心这个Activity是否被系统销毁了

1.2.2谁能证明你存在过?onSaveInstanceState()!

在框架调用onSaveInstanceState()方法时,给这个方法传递了一个Bundle对象。Activity可以通过这个对象来存储它的状态,而且Activity把它的dialogs、fragments以及views的状态都保存在这个对象里面。当这个函数返回时,系统打包这个Bundle对象通过一个Binder接口传递给系统服务处理,然后它会被安全的存储下来。当系统决定重新创建这个Activity的时候,它会给这个应用传回一个相同的Bundle对象,通过这个对象可以重新装载Activity销毁时的状态

1.2.3报错的原因——bundle快照机制

那为什么会抛出这个异常呢?这个问题源于这样的事实,Bundle对象代表一个Activity在调用onSaveInstanceState()方法的一个瞬间快照,仅此而已。这意味着,当你在onSaveInstanceState()方法调用后会调用FragmentTransaction的commit方法。这个transaction将不会被记住,因为它没有在第一时间记录为这个Activity的状态的一部分。从用户的角度来看,这个transaction将会丢失,可能导致UI状态丢失。为了保证用户的体验,Android不惜一切代价避免状态的丢失。因此,无论什么时候发生,都将简单的抛出一个IllegalStateException异常

简单来说就是:activity可以销毁之前,有个方法通过bundle记录了activity的各种信息,但这个bundle是个快照(幸好玩wow知道这是啥),你在这个bundle对象创建完了,再去commit,肯定不会被记住,因为bundle都 定好了;而系统果断不能接受这种异端(否则就可能回出现ui状态的丢失),所以必须报错

1.2.4如何避免

1)        建议一:(一般不会有这个困扰)

假如:如果你的应用要求在除onCreate()函数之外的其他Activity生命周期函数中提交transaction,你可以在FragmentActivity的onResumeFragments()函数或者Activity的onPostResume()函数中提交。不过,大部分的应用仅仅在onCreate()方法被调用的开始时间提交transactions,或者在相应用户输入的时候,因此将不可能碰到任何问题。

2)        建议二:避免在异步回调函数中提交transactions

举个栗子:

                        i.              一个Activity执行一个AsyncTask

                      ii.              用户按下“Home”键,导致Activity的onSaveInstanceState()和onStop()方法被调用

                    iii.              AsyncTask完成并且onPostExecute方法被调用,而它没有意识到Activity已经结束了

                     iv.              在onPostExecute函数中提交的FragmentTransaction,导致抛出一个异常

如果你的应用需要在这些回调函数中执行transaction而没有简单的方法可以确保这个回调函数不好在onSaveInstanceState()之后调用,那么你可能需要诉诸于使用commitAllowingStateLoss方法并且处理可能发生的状态丢失。

 

2.android.R.id.content

 

在安卓布局文件中添加控件时,系统定义的此Fragment的id为android.R.id.content,所以调用findViewById(android.R.id.content)可以得到此Fragment的view

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值