java.util.ConcurrentModificationException ArrayList
问题日志
07-11 22:36:10.067 8431 8431 E AndroidRuntime: FATAL EXCEPTION: main07-11 22:36:10.067 8431 8431 E AndroidRuntime: Process: com.xiaomi.micolauncher, PID: 843107-11 22:36:10.067 8431 8431 E AndroidRuntime: java.util.ConcurrentModificationException07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.util.ArrayList.writeObject(ArrayList.java:766)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1037)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1552)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeSerializable(Parcel.java:1709)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeValue(Parcel.java:1662)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeArrayMapInternal(Parcel.java:875)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1579)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Bundle.writeToParcel(Bundle.java:1233)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeBundle(Parcel.java:915)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.support.v4.app.d.writeToParcel(FragmentState.java:124)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeTypedObject(Parcel.java:1516)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeTypedArray(Parcel.java:1497)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.support.v4.app.c.writeToParcel(FragmentManager.java:639)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeParcelable(Parcel.java:1683)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeValue(Parcel.java:1589)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeArrayMapInternal(Parcel.java:875)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1579)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Bundle.writeToParcel(Bundle.java:1233)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.app.IActivityManager$Stub$Proxy.activityStopped(IActivityManager.java:3985)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:144)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:873)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:99)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Looper.loop(Looper.java:209)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6702)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)07-11 22:36:10.067 8431 8431 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)
问题关键描述
java.util.ConcurrentModificationException
java.util.ArrayList.writeObject(ArrayList.java:766)
问题原因
ArrayList在读的同时集合改变了,具体可以看ArrayList源码
问题发生源头
日志没有太多,只有如上一段最关键的日志。首先既然是并发处理了ArrayList,那么一定有一个地方writeObject,另一个地方put或者remove了。仔细分析日志,大概筛选出writeObject的地方:
07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Bundle.writeToParcel(Bundle.java:1233)
07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.app.IActivityManager S t u b Stub StubProxy.activityStopped(IActivityManager.java:3985)
07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:144)
日志显示最早问题开始于PendingTransactionActions,然后到了IActivityManager,它的实现类是ActivityManagerService.java,看下它的activityStopped方法:
@Override public final void activityStopped(IBinder token, Bundle icicle, PersistableBundle persistentState, CharSequence description) { if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token); // Refuse possible leaked file descriptors if (icicle != null && icicle.hasFileDescriptors()) { throw new IllegalArgumentException("File descriptors passed in Bundle"); } final long origId = Binder.clearCallingIdentity(); synchronized (this) { final ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { r.activityStoppedLocked(icicle, persistentState, description); } } trimApplications(); Binder.restoreCallingIdentity(origId); }
如果是activityStopped内部某个方法出错,那么如上的runtime日志应该具体把哪个方法记录下来,但是没有。说明
IActivityManager代理中出了问题,这就看不到代码了。
我们再顺着日志网上看,有这么一个关键日志:
07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeBundle(Parcel.java:915)
07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.support.v4.app.d.writeToParcel(FragmentState.java:124)
07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeTypedObject(Parcel.java:1516)
07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.os.Parcel.writeTypedArray(Parcel.java:1497)
07-11 22:36:10.067 8431 8431 E AndroidRuntime: at android.support.v4.app.c.writeToParcel(FragmentManager.java:639)
我们看下FragmentState.writeToParcel
Bundle mSavedFragmentState; @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mClassName); dest.writeInt(mIndex); dest.writeInt(mFromLayout ? 1 : 0); dest.writeInt(mFragmentId); dest.writeInt(mContainerId); dest.writeString(mTag); dest.writeInt(mRetainInstance ? 1 : 0); dest.writeInt(mDetached ? 1 : 0); dest.writeBundle(mArguments); dest.writeInt(mHidden ? 1 : 0); dest.writeBundle(mSavedFragmentState); }
writeBundle报错的,根据日志的代码行数是mArguments这个值变化了。
日志结论
到这里日志给出的信息基本没啥再多的东西了。其中有一个线程操作了PendingTransactionActions,这个方法最终会对FragmentState.mSavedFragmentState做Parcelable。同时,有其他线程对FragmentState.mArguments进行了增加或者删除。
源码分析
一、首先分析哪里调用了PendingTransactionActions,先看下PendingTransactionActions.java
public StopInfo getStopInfo() { return mStopInfo; } public void setStopInfo(StopInfo stopInfo) { mStopInfo = stopInfo; } public static class StopInfo implements Runnable { @Override public void run() { // Tell activity manager we have been stopped. try { if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity); // TODO(lifecycler): Use interface callback instead of AMS. ActivityManager.getService().activityStopped( mActivity.token, mState, mPersistentState, mDescription); ...... } }
StopInfo是个runnable,调用它只能通过get和set方法,看下哪里set和get的。经过一番查找,发现在ActivityThread.java
@Override public void handleStopActivity(IBinder token, boolean show, int configChanges, PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) { ...... final StopInfo stopInfo = new StopInfo(); stopInfo.setActivity(r); stopInfo.setState(r.state); stopInfo.setPersistentState(r.persistentState); pendingActions.setStopInfo(stopInfo); mSomeActivitiesChanged = true; } /** * Schedule the call to tell the activity manager we have stopped. We don't do this * immediately, because we want to have a chance for any other pending work (in particular * memory trim requests) to complete before you tell the activity manager to proceed and allow * us to go fully into the background. */ @Override public void reportStop(PendingTransactionActions pendingActions) { mH.post(pendingActions.getStopInfo()); }
ActivityTread.handleStopActivity时会创建StopInfo,随后会有类调用reportStop通过Handler执行。handleStopActivity这里就不深挖了,肯定是给activity分发stop状态。看下哪里调用了reportStop,代码在StopActivityItem.java
public class StopActivityItem extends ActivityLifecycleItem { private static final String TAG = "StopActivityItem"; private boolean mShowWindow; private int mConfigChanges; @Override public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop"); client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions, true /* finalStateRequest */, "STOP_ACTIVITY_ITEM"); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { client.reportStop(pendingActions); } @Override public int getTargetState() { return ON_STOP; } // ObjectPoolItem implementation private StopActivityItem() {} /** Obtain an instance initialized with provided params. */ public static StopActivityItem obtain(boolean showWindow, int configChanges) { StopActivityItem instance = ObjectPool.obtain(StopActivityItem.class); if (instance == null) { instance = new StopActivityItem(); } instance.mShowWindow = showWindow; instance.mConfigChanges = configChanges; return instance; }
这里有两个重要方法,execute和postExecute,ClientTransactionHandler是什么?ActivityThread extends ClientTransactionHandler,很明显,它可以是ActivityThread。
调用StopActivityItem的地方在TransactionExecutorHelper.java
/** Get the lifecycle state request to match the current state in the end of a transaction. */ public static ActivityLifecycleItem getLifecycleRequestForCurrentState(ActivityClientRecord r) { final int prevState = r.getLifecycleState(); final ActivityLifecycleItem lifecycleItem; switch (prevState) { // TODO(lifecycler): Extend to support all possible states. case ON_PAUSE: lifecycleItem = PauseActivityItem.obtain(); break; case ON_STOP: lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(), 0 /* configChanges */); break; default: lifecycleItem = ResumeActivityItem.obtain(false /* isForward */); break; } return lifecycleItem; }
调用TransactionExecutorHelper.getLifecycleRequestForCurrentState的地方在ActivityThread.java
/** Performs the activity relaunch locally vs. requesting from system-server. */ private void handleRelaunchActivityLocally(IBinder token) { ...... // Make sure to match the existing lifecycle state in the end of the transaction. final ActivityLifecycleItem lifecycleRequest = TransactionExecutorHelper.getLifecycleRequestForCurrentState(r); // Schedule the transaction. final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token); transaction.addCallback(activityRelaunchItem); transaction.setLifecycleStateRequest(lifecycleRequest); executeTransaction(transaction); }
调用handleRelaunchActivityLocally的地方在ActivityThread.handleRelaunchActivityLocally
/** * Post a message to relaunch the activity. We do this instead of launching it immediately, * because this will destroy the activity from which it was called and interfere with the * lifecycle changes it was going through before. We need to make sure that we have finished * handling current transaction item before relaunching the activity. */ void scheduleRelaunchActivity(IBinder token) { sendMessage(H.RELAUNCH_ACTIVITY, token); }
到这里就不往上看了,很明显了,当Activity被重新launcher的时,如果ActivityClientRecord.getLifecycleState是Stop状态会最终调用到日志中的IActivityManager.activityStopped,从而会触发Activity内的FragmentManger,然后触发FragmentState.writeToParcel。这样出问题的ArrayList的遍历读的地方逻辑梳理完了,当Activity处于Stop状态变更的时候,会在stop执行完调用IActivityManager.activityStopped,打印一些日志什么的吧。
二、我们看下异步会触发FragmentState.mArguments的地方
调用FragmentState.mArguments变动的地方,在FragmentState.java
public Fragment instantiate(FragmentHostCallback host, FragmentContainer container, Fragment parent, FragmentManagerNonConfig childNonConfig) { if (mInstance == null) { final Context context = host.getContext(); if (mArguments != null) { mArguments.setClassLoader(context.getClassLoader()); } if (container != null) { mInstance = container.instantiate(context, mClassName, mArguments); } else { mInstance = Fragment.instantiate(context, mClassName, mArguments); } ...... } }
调用instantiate地方在FragmentManager.FragmentManagerImpl.java
SparseArray<Fragment> mActive; void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) { // Build the full list of active fragments, instantiating them from // their saved state. mActive = new SparseArray<>(fms.mActive.length); for (int i=0; i<fms.mActive.length; i++) { FragmentState fs = fms.mActive[i]; if (fs != null) { FragmentManagerNonConfig childNonConfig = null; if (childNonConfigs != null && i < childNonConfigs.size()) { childNonConfig = childNonConfigs.get(i); } Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig); if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f); mActive.put(f.mIndex, f); // Now that the fragment is instantiated (or came from being // retained above), clear mInstance in case we end up re-restoring // from this FragmentState again. fs.mInstance = null; } } }
调用的地方restoreAllState地方是Fragment.java
void restoreChildFragmentState(@Nullable Bundle savedInstanceState, boolean provideNonConfig) { if (savedInstanceState != null) { Parcelable p = savedInstanceState.getParcelable(Activity.FRAGMENTS_TAG); if (p != null) { if (mChildFragmentManager == null) { instantiateChildFragmentManager(); } mChildFragmentManager.restoreAllState(p, provideNonConfig ? mChildNonConfig : null); mChildNonConfig = null; mChildFragmentManager.dispatchCreate(); } } } void performCreate(Bundle savedInstanceState) { ...... if (version < Build.VERSION_CODES.N) { restoreChildFragmentState(savedInstanceState, false); } } public void onCreate(@Nullable Bundle savedInstanceState) { mCalled = true; final Context context = getContext(); final int version = context != null ? context.getApplicationInfo().targetSdkVersion : 0; if (version >= Build.VERSION_CODES.N) { restoreChildFragmentState(savedInstanceState, true); if (mChildFragmentManager != null && !mChildFragmentManager.isStateAtLeast(Fragment.CREATED)) { mChildFragmentManager.dispatchCreate(); } } }
导致mArguments变动的地方有两个,一个是Fragment.onCreate,另一个是Fragment.performCreate。
结论:基本逻辑分析完了。AndroidRuntime的日志也没有更多了。触发此问题的时机:
1、Activity被重新launcher,同时Activity状态是Stop
2、Fragment到了onCreate状态,会恢复所有被FragmentManager管理的Fragment。也就是说在Activity处于Stop状态时,对Fragment栈进行了变动。
剩下的就是看AndroidRuntime上边的日志,确定同时发生场景1和2的地方。
问题原因追查
经过仔细查找。最终发现是代码中往Fragment.setArgument中put了List。而这个List是put的引用对象,这个List后台会变动。也就低概率导致了这个问题。所以使用Bundler切记不要传递可变对象的引用。
更多相关文章
- android facebook authorize 时禁止调用facebook app
- Android(安卓)support和Androidx库不能共存问题【android日志小
- android 调用webservice
- 三步搞定:Vue.js调用Android原生操作
- android API之KeyguardManager简介
- Android(安卓)调用Camera和相册
- Android(安卓)-- Activity官方文档简译
- Android关于OnTouch 和OnClick同时调用冲突的解决方案
- Android与Javascript交互示例(一)