问题日志

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切记不要传递可变对象的引用。

更多相关文章

  1. android facebook authorize 时禁止调用facebook app
  2. Android(安卓)support和Androidx库不能共存问题【android日志小
  3. android 调用webservice
  4. 三步搞定:Vue.js调用Android原生操作
  5. android API之KeyguardManager简介
  6. Android(安卓)调用Camera和相册
  7. Android(安卓)-- Activity官方文档简译
  8. Android关于OnTouch 和OnClick同时调用冲突的解决方案
  9. Android与Javascript交互示例(一)

随机推荐

  1. Android桌面悬浮窗效果实现,仿360手机卫士
  2. Android(安卓)自定义 View 之使用 Surfac
  3. android中ListView点击和里边按钮点击不
  4. Android通过应用程序创建快捷方式的方法
  5. 写给Android开发者的Windows Phone开发秘
  6. 创建非阻塞的Socket服务器
  7. android intent和intent action大全【转
  8. Android添加新语言
  9. android手机信号强度
  10. Android(安卓)面向协议编程