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 为空,进一步导致崩溃。
解决方法:
解决方案可以判空,但是这样不一定能完全避免崩溃。因为不确定哪一个个时刻一定在前。