Android OEM Fragment 填坑之路

本文记录了在使用FragmentV4过程中遇到的几个典型崩溃案例,并深入分析了崩溃原因,包括递归执行待处理事务、尝试更改已附加Fragment的容器ID及与RecyclerView结合使用时的常见问题。
摘要由CSDN通过智能技术生成

Fragment V4 填坑实录

Fragment 会出很多神一样的崩溃,是正常人无法理解的,尤其是和RecyclerView 合用嵌套的时候。
先贴出来几个神一样的崩溃:

崩溃 1

java.lang.IllegalStateException: Recursive entry to executePendingTransactions
10-18 14:09:03.116  8856  8856 I         :  at android.support.v4.app.FragmentManagerImpl.g(FragmentManager.java:1544)
10-18 14:09:03.116  8856  8856 I         :  at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:493)
10-18 14:09:03.116  8856  8856 I         :  at android.os.Handler.handleCallback(Handler.java:739)
10-18 14:09:03.116  8856  8856 I         :  at android.os.Handler.dispatchMessage(Handler.java:95)
10-18 14:09:03.116  8856  8856 I         :  at android.os.Looper.loop(Looper.java:148)
10-18 14:09:03.116  8856  8856 I         :  at android.app.ActivityThread.main(ActivityThread.java:5441)
10-18 14:09:03.116  8856  8856 I         :  at java.lang.reflect.Method.invoke(Native Method)
10-18 14:09:03.116  8856  8856 I         :  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
10-18 14:09:03.116  8856  8856 I         :  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)

崩溃2:

Msg:Can't change container ID of fragment VideoFragment{acbc03f #0 id=0x4}: was 4 now 5 DumpKey:3919977355=====
11-01 02:04:26.437  8949  8949 I         : [UNEXC]/ java.lang.IllegalStateException: Can't change container ID of fragment VideoFragment{acbc03f #0 id=0x4}: was 4 now 5
11-01 02:04:26.437  8949  8949 I         :  at android.support.v4.app.BackStackRecord.a(BackStackRecord.java:427)
11-01 02:04:26.437  8949  8949 I         :  at android.support.v4.app.BackStackRecord.a(BackStackRecord.java:404)
11-01 02:04:26.437  8949  8949 I         :  at 
video.cm.a.a(PlayerManager.java:174)
11-01 02:04:26.437  8949  8949 I         :  at 
video.cm.a.a(PlayerManager.java:37)
11-01 02:04:26.437  8949  8949 I         :  at 
vidwo.cm.e.run(PlayerManager.java:101)
11-01 02:04:26.437  8949  8949 I         :  at android.os.Handler.handleCallback(Handler.java:739)
11-01 02:04:26.437  8949  8949 I         :  at android.os.Handler.dispatchMessage(Handler.java:95)
11-01 02:04:26.437  8949  8949 I         :  at android.os.Looper.loop(Looper.java:148)
11-01 02:04:26.437  8949  8949 I         :  at android.app.ActivityThread.main(ActivityThread.java:5441)
11-01 02:04:26.437  8949  8949 I         :  at java.lang.reflect.Method.invoke(Native Method)
11-01 02:04:26.437  8949  8949 I         :  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
11-01 02:04:26.437  8949  8949 I         :  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
11-01 02:04:26.442  8949  8949 I         : [UNEXC]/ ===== uncaughtException.TraceEnd:Thread[main,5,main] =====

崩溃3:

: No package identifier when getting name for resource number 0x00000004
11-01 02:04:24.418  8949  8949 W System.err: android.content.res.Resources$NotFoundException: Unable to find resource ID #0x4
11-01 02:04:24.424 18179 18179 I Finsky  : [1] com.google.android.finsky.services.bd.a(1071): Restore complete with 0 success and 0 failed.
11-01 02:04:24.426  8949  8949 W System.err:    at android.content.res.Resources.getResourceName(Resources.java:2272)
11-01 02:04:24.426  8949  8949 W System.err:    at android.support.v4.app.FragmentManagerImpl.a(FragmentManager.java:1018)
11-01 02:04:24.426  8949  8949 W System.err:    at android.support.v4.app.FragmentManagerImpl.a(FragmentManager.java:1207)
11-01 02:04:24.426  8949  8949 W System.err:    at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738)
11-01 02:04:24.426  8949  8949 W System.err:    at android.support.v4.app.FragmentManagerImpl.g(FragmentManager.java:1572)
11-01 02:04:24.426  8949  8949 W System.err:    at android.support.v4.app.FragmentManagerImpl.b(FragmentManager.java:545)
11-01 02:04:24.426  8949  8949 W System.err:    at video.cm.a.a(PlayerManager.java:177)
11-01 02:04:24.426  8949  8949 W System.err:    at video.cm.a.a(PlayerManager.java:37)
11-01 02:04:24.426  8949  8949 W System.err:    at video.cm.e.run(PlayerManager.java:101)
11-01 02:04:24.426  8949  8949 W System.err:    at android.os.Handler.handleCallback(Handler.java:739)
11-01 02:04:24.426  8949  8949 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:95)
11-01 02:04:24.426  8949  8949 W System.err:    at android.os.Looper.loop(Looper.java:148)
11-01 02:04:24.426  8949  8949 W System.err:    at android.app.ActivityThread.main(ActivityThread.java:5441)
11-01 02:04:24.426  8949  8949 W System.err:    at java.lang.reflect.Method.invoke(Native Method)
11-01 02:04:24.426  8949  8949 W System.err:    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
11-01 02:04:24.426  8949  8949 W System.err:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)

怎么样这几个是不是很头大,没关系我们仔细看代码。
首先,崩溃1,对于崩溃一来说,崩溃原始代码是

/**
     * Only call from main thread!
     */
    public boolean execPendingActions() {
        if (mExecutingActions) {
            throw new IllegalStateException("Recursive entry to executePendingTransactions");
        }

        if (Looper.myLooper() != mHost.getHandler().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();
                mHost.getHandler().removeCallbacks(mExecCommit);
            }

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

发现对mExecutingActions 的赋值只有本函数中,所以对没有其他函数能够赋值mExecutingActions。
所以只有一个可能性就是上次执行代码的时候发生了崩溃,导致mExecutingActions的状态没有重置。
所以对于崩溃1来说,应该来自于上次崩溃。检查代码发现了崩溃2。崩溃的原因很简单,向不同Id的View中添加同一个Fragment 。
代码如下:

//添加第一个
 final FragmentManager supportFragmentManager = getSupportFragmentManager();
        final FragmentTransaction fragmentTransaction =    supportFragmentManager.beginTransaction();
        mFragment = new Fragment();
        fragmentTransaction.add(viewId, mFragment);
        fragmentTransaction.commitAllowingStateLoss();
        //添加第二个
        fragmentTransaction =   supportFragmentManager.beginTransaction();
        fragmentTransaction.add(viewId1, mFragment);
        fragmentTransaction.commitAllowingStateLoss();



解决方法:

所以崩溃很简单就能解决,新建一个Fragment 或者将Fragment remove 再添加。

对于崩溃3来说,是Fragment 比较头疼的一个问题,就是Fragment 与RecyclerView 联合使用的问题。
具体来说:
1. RecyclerView 在item 移出可见范围的时候,会View会OnDetectFromWindow。这个时候如果调用onFindViewById 的时候会为null。
2. Fragment 延迟创建的时候,会调用OnFindViewById 。
如果按照1—>2 的顺序调用会知道Fragment containerView 为空,进一步导致崩溃。
Crash代码如下

解决方法:

解决方案可以判空,但是这样不一定能完全避免崩溃。因为不确定哪一个个时刻一定在前。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值