Fragment使用过程遇到的问题汇总和思考

onActivityResult中创建Fragment

一个异常的分析

A activity跳转到B activity,然后又返回到A activity,在A activity的onActivityResult中,给A添加一个Fragment,由于在Fragment的onCreateView方法中有个异常,导致了崩溃

07-15 15:48:44.699  3430  3430 D AndroidRuntime: Shutting down VM
07-15 15:48:44.702  3430  3430 E AndroidRuntime: FATAL EXCEPTION: main
07-15 15:48:44.702  3430  3430 E AndroidRuntime: Process: com.sohu.sohuvideo, PID: 3430
07-15 15:48:44.702  3430  3430 E AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.sohu.sohuvideo/com.sohu.sohuvideo.ui.BuyVipActivity}: java.lang.NullPointerException: uriString
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3992)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4024)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:51)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:145)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1926)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:214)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:6990)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: Caused by: java.lang.NullPointerException: uriString
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.net.Uri$StringUri.<init>(Uri.java:490)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.net.Uri$StringUri.<init>(Uri.java:480)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.net.Uri.parse(Uri.java:452)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at com.sohu.sohuvideo.paysdk.ui.dialog.ActivityCommodityPayResultDialogFragment.onCreateView(ActivityCommodityPayResultDialogFragment.java:159)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2169)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1992)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1947)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentController.execPendingActions(FragmentController.java:447)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at androidx.fragment.app.FragmentActivity.onResume(FragmentActivity.java:458)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at com.sohu.sohuvideo.ui.BaseActivity.onResume(BaseActivity.java:285)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at com.sohu.sohuvideo.ui.BuyVipActivity.onResume(BuyVipActivity.java:316)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1412)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.Activity.performResume(Activity.java:7611)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3984)
07-15 15:48:44.702  3430  3430 E AndroidRuntime: 	... 11 more

只是单纯解决这个异常很简单。不过,这边文章想说的是,我们可以从堆栈信息中了解一些知识。
从B Activity返回到A activity是先执行A的onActivityResult然后是onResume方法,在onActivityResult中把Fragment添加到A activity中的。
那为啥Fragment的onCreateView的执行过程是从Activity的onResume方法开始的呢?
这个就要从我们使用的Fragment commit方式说起了,我们采用的是commitAllowingStateLoss,commitAllowingStateLoss和commit方式都不是及时的,是主线程异步的,主线程异步指的是不在当前消息的执行体内,而是在Looper的下一个消息执行体内,而且会把之前所有提交还没执行的commit都执行。这个可以从源码中知道

    /**
     * Schedules the execution when one hasn't been scheduled already. This should happen
     * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
     * a postponed transaction has been started with
     * {@link Fragment#startPostponedEnterTransition()}
     */
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void scheduleCommit() {
        synchronized (mPendingActions) {
            boolean postponeReady =
                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
            boolean pendingReady = mPendingActions.size() == 1;
            if (postponeReady || pendingReady) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
                updateOnBackPressedCallbackEnabled();
            }
        }
    }

    private Runnable mExecCommit = new Runnable() {
        @Override
        public void run() {
            execPendingActions(true);
        }
    };

    /**
     * Only call from main thread!
     */
    boolean execPendingActions(boolean allowStateLoss) {
        ensureExecReady(allowStateLoss);

        boolean didSomething = false;
        while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
            mExecutingActions = true;
            try {
                removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
            } finally {
                cleanupExec();
            }
            didSomething = true;
        }

        updateOnBackPressedCallbackEnabled();
        doPendingDeferredStart();
        mFragmentStore.burpActive();

        return didSomething;
    }

如果按照真是按照上面讲的逻辑执行的话,执行逻辑应该是Handler的callback方法,mHost.getHandler()是new出来的一个Handler,不是ActivityThread$H这个handler,大家想想,平时new一个handler执行一个runnable是一个怎么样的调用堆栈。
可是,实际情况走的却是onResume,这个就需要看看onResume源码了

    /**
     * Dispatch onResume() to fragments.  Note that for better inter-operation
     * with older versions of the platform, at the point of this call the
     * fragments attached to the activity are <em>not</em> resumed.
     */
    @Override
    protected void onResume() {
        super.onResume();
        mResumed = true;
        mFragments.noteStateNotSaved();
        mFragments.execPendingActions();
    }

看到没,也调用了execPendingActions方法。同时也说明了一个问题,onActivityResult和onResume是在一个消息的执行体内,因为如果不在,就很难保证是先执行onResume还是先执行mExecCommit。而且我们也知道commit是主线程异步,也不只是绝对的。

把commitAllowingStateLoss换成commitNowAllowingStateLoss,让Fragment立即执行

07-15 16:22:23.291  6176  6176 E AndroidRuntime: FATAL EXCEPTION: main
07-15 16:22:23.291  6176  6176 E AndroidRuntime: Process: com.sohu.sohuvideo, PID: 6176
07-15 16:22:23.291  6176  6176 E AndroidRuntime: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { (has extras) }} to activity {com.sohu.sohuvideo/com.sohu.sohuvideo.ui.BuyVipActivity}: java.lang.NullPointerException: uriString
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.app.ActivityThread.deliverResults(ActivityThread.java:4588)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.app.ActivityThread.handleSendResult(ActivityThread.java:4630)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:49)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1926)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:214)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:6990)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: Caused by: java.lang.NullPointerException: uriString
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.net.Uri$StringUri.<init>(Uri.java:490)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.net.Uri$StringUri.<init>(Uri.java:480)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.net.Uri.parse(Uri.java:452)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at com.sohu.sohuvideo.paysdk.ui.dialog.ActivityCommodityPayResultDialogFragment.onCreateView(ActivityCommodityPayResultDialogFragment.java:159)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:320)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2169)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1992)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1947)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1818)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:303)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at com.sohu.sohuvideo.paysdk.ui.dialog.ActivityCommodityPayResultDialogFragment$Builder.create(ActivityCommodityPayResultDialogFragment.java:109)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at com.sohu.sohuvideo.ui.BuyVipActivity.activityCommodityPaySuccess(BuyVipActivity.java:2055)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at com.sohu.sohuvideo.ui.BuyVipActivity.onActivityResult(BuyVipActivity.java:1246)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.app.Activity.dispatchActivityResult(Activity.java:7801)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	at android.app.ActivityThread.deliverResults(ActivityThread.java:4581)
07-15 16:22:23.291  6176  6176 E AndroidRuntime: 	... 11 more

这个堆栈一看,就知道Fragment的操作在onActivityResult的方法内就直接执行了。这个很好理解

参考

你真的懂 Fragment 吗?—— AndroidX Fragment 核心原理分析
Fragment 的过去、现在和将来
【背上Jetpack之Fragment】从源码角度看 Fragment 生命周期 AndroidX Fragment1.2.2源码分析

正确创建Fragment的时机

这个章节涉及的问题是在onActivityResult中创建Fragment,有个问题是,在onActivityResult(或onResume)时机的时候,activity的state还没有恢复(activity可能被系统销毁了),commit Fragment就会报错。
下面这两篇文章就讨论了这个问题
"Failure Delivering Result " - onActivityForResult
Fragment Transactions & Activity State Loss

Fragment State Loss

fragment state loss这个问题,在使用fragment的时候,总是会时不时遇到。让你不再俱怕Fragment State Loss进行了深入的分析

onBackPressed不能乱用

onBackPressed是响应到返回键的时候调用的,里面有对Fragment的退栈处理。
所以,在实际使用中,得明确需求,你是想模拟back键呢还是想关闭activity,如果只是想关闭activity,直接调finish。自己开发中,有在网络接口回调中,想关闭activity,结果调用的是onBackPressed,结果出现两类错误,一类错误是,java.lang.IllegalStateException Can not perform this action after onSaveInstanceState 这是因为,在低版本上,onBackPressed做退栈处理的时候,没有想检查activity的state导致的,二类错误是java.lang.NullPointerException Attempt to invoke virtual method 'android.os.Handler android.app.FragmentHostCallback.getHandler()' on a null object reference

根本问题是,应该调用finish,却调用onBackPressed

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值