美文网首页andnroid
解决IllegalStateException: Can not

解决IllegalStateException: Can not

作者: 有爱的梦_大东 | 来源:发表于2021-08-02 11:03 被阅读0次

    实际项目出现此问题

    一般是FragmentTransaction事务提交时报错,将commit换为commitAllowingStateLoss

    从这里可以引申出Fragment的FragmentTransaction 的commit()和commitAllowingStateLoss()以及commitNow()和commitNowAllowingStateLoss()

    commit

    只能在activity存储它的状态(onSaveInstanceState(),当用户要离开activity时)之前调用commit(),如果在存储状态之后调用commit(),将会抛出一个异常,这是因为当activity再次被恢复时commit之后的状态将丢失。如果丢失也没关系,那么使用commitAllowingStateLoss()方法,commit和CommitNow都会抛出异常,如果在onSaveInstanceState()后执行的话。allowStateLoss=false,方法后面会执行检查checkStateLoss(),就是已经保存状态或stop的就会抛出异常IllegalStateException

    commitNow

    commitNow不允许addToBackStack,commitNow是不允许加入BackStack中去的,会将mAddToBackStack标志设置为false

    commitAllowingStateLoss

    同commit一样调用内部的commitInternal()方法,只不过传递的参数不同,commitAllowStateLoss的allowStateLoss是true,允许丢失状态不做检查,所以不会抛异常

    代码

    @Override
        public int commit() {
            return commitInternal(false);
        }
    //允许转台丢失
        @Override
        public int commitAllowingStateLoss() {
            return commitInternal(true);
        }
    

    可以看到两方法都调用commitInternal方法,参数是allowStateLoss的boolean类型

    int commitInternal(boolean allowStateLoss) {
            if (mCommitted) throw new IllegalStateException("commit already called");
            if (FragmentManager.isLoggingEnabled(Log.VERBOSE)) {
                Log.v(TAG, "Commit: " + this);
                LogWriter logw = new LogWriter(TAG);
                PrintWriter pw = new PrintWriter(logw);
                dump("  ", pw);
                pw.close();
            }
            mCommitted = true;
            if (mAddToBackStack) {
                mIndex = mManager.allocBackStackIndex();
            } else {
                mIndex = -1;
            }
    //加入队列
            mManager.enqueueAction(this, allowStateLoss);
            return mIndex;
        }
    

    可以看到是通过 mManager.enqueueAction(this, allowStateLoss);来执行

    void enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss) {
            if (!allowStateLoss) {
                if (mHost == null) {
                    if (mDestroyed) {
                        throw new IllegalStateException("FragmentManager has been destroyed");
                    } else {
                        throw new IllegalStateException("FragmentManager has not been attached to a "
                                + "host.");
                    }
                }
                checkStateLoss();
            }
            synchronized (mPendingActions) {
                if (mHost == null) {
                    if (allowStateLoss) {
                        // This FragmentManager isn't attached, so drop the entire transaction.
                        return;
                    }
                    throw new IllegalStateException("Activity has been destroyed");
                }
                mPendingActions.add(action);
                scheduleCommit();
            }
        }
    

    allowStateLoss参数是来执行检测操作的 checkStateLoss();这就是报错的地方

    private void checkStateLoss() {
            if (isStateSaved()) {
                throw new IllegalStateException(
                        "Can not perform this action after onSaveInstanceState");
            }
        }
    

    如果activity的状态被保存了,这里再提交就会检查这个状态,符合这个条件就抛出一个异常来终止应用进程。也就是说在activity调用了onSaveInstanceState()之后,再commit一个事务就会出现该异常。那如果不想抛出异常,也可以很简单调用commitAllowingStateLoss()方法来略过这个检查就可以了,但是这是危险的,如果activity随后需要从它保存的状态中恢复,这个commit是会丢失的。因此它仅仅适用在ui状态的改变对用户来说是可以接受的,允许丢失一部分状态。

    总结

    1.在Activity的生命周期方法中提交事务要小心,越早越好,比如onCreate。尽量避免在onActivityResult()方法中提交。
    2.避免在异步的回调方法中执行commit,因为他们感知不到当前Activity生命周期的状态。
    3.使用commitAllowingStateLoss()代替commit()。相比于异常crash,UI状态的改变对用户来说是可以接受的。
    4.如果你需要在Activity执行完onSaveInstanceState()之后还要进行提交,而且不关心恢复时是否会丢失此次提交,那么可以使用commitAllowingStateLoss()或commitNowAllowingStateLoss()。

    commitNow以及commitNowAllowingstateLoss()

    在API_24版本FragmentTranslation里添加了该两个方法:
    该方法不支持加入BackStack回退栈中disallowAddToBackStack()。
    官方更推荐使用commitNow()和commitNowAllowingStateLoss()来代替先执行commit()/commitAllowingStateLoss()。我们在解决的时候看看最小支持版本

    这个问题也会出现在dialog的show和dismiss中(DialogFragment)

    解决,在重写show和dismiss

    override fun show(manager: FragmentManager, tag: String?) {
            if (isAdded) {
                return
            }
            try {
                val mDismissed = DialogFragment::class.java.getDeclaredField("mDismissed")
                mDismissed.isAccessible = true
                mDismissed.setBoolean(this, false)
            } catch (e: Exception) {
                e.printStackTrace()
            }
            try {
                val mShownByMe = DialogFragment::class.java.getDeclaredField("mShownByMe")
                mShownByMe.isAccessible = true
                mShownByMe.setBoolean(this, true)
            } catch (e: Exception) {
                e.printStackTrace()
            }
            val ft = manager.beginTransaction()
            ft.add(this, tag)
            ft.commitAllowingStateLoss()
        }
    
    override fun onDismiss(dialog: DialogInterface) {
            super.onDismiss(dialog)
            dismissAllowingStateLoss()
        
    

    相关文章

      网友评论

        本文标题:解决IllegalStateException: Can not

        本文链接:https://www.haomeiwen.com/subject/xdmdvltx.html