java.util.ConcurrentModificationException ArrayList

问题日志

07-11 22:36:10.067 8431 8431 E AndroidRuntime: FATAL EXCEPTION: main
07-11 22:36:10.067 8431 8431 E AndroidRuntime: Process: com.xiaomi.micolauncher, PID: 8431
07-11 22:36:10.067 8431 8431 E AndroidRuntime: java.util.ConcurrentModificationException
07-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,android)