在WindowManagerGlobal的setStoppedState里面出现IndexOutOfBoundsException。

问题描述 投票:2回答:1

我们应用中的顶部崩溃只发生在Android 9上,而且只发生在少数设备上,比如中兴、TCL、海信、BLU、HYUNDAI。

由于我们无法接触到这些设备,所以我们无法重现这种情况,而且由于它发生在Android框架内,我们不确定这是我们代码中的错误(虽然一定是这样,因为我们在Stackoverflow中找不到任何类似的崩溃)。

Fatal Exception: java.lang.RuntimeException: Unable to stop activity {MyActivity}: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
       at android.app.ActivityThread.callActivityOnStop(ActivityThread.java:4213)
       at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:4183)
       at android.app.ActivityThread.handleStopActivity(ActivityThread.java:4263)
       at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:192)
       at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
       at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:193)
       at android.app.ActivityThread.main(ActivityThread.java:6852)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:504)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
       at java.util.ArrayList.get(ArrayList.java:437)
       at android.view.WindowManagerGlobal.setStoppedState(WindowManagerGlobal.java:604)
       at android.app.Activity.performStop(Activity.java:8831)
       at android.app.ActivityThread.callActivityOnStop(ActivityThread.java:4205)
       at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:4183)
       at android.app.ActivityThread.handleStopActivity(ActivityThread.java:4263)
       at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:192)
       at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
       at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:193)
       at android.app.ActivityThread.main(ActivityThread.java:6852)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:504)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

有谁经历过类似的崩溃,或者知道根本原因是什么?

android android-activity crash android-lifecycle android-windowmanager
1个回答
1
投票

这里是 崩溃的代码:

public void setStoppedState(IBinder token, boolean stopped) {
    synchronized (mLock) {
        int count = mViews.size();
        for (int i = 0; i < count; i++) {
            if (token == null || mParams.get(i).token == token) {
                ViewRootImpl root = mRoots.get(i);
                root.setWindowStopped(stopped);
            }
        }
    }
}

(行号与你的异常信息不完全一致,因为我不知道你的设备使用的代码的确切版本。)

假设我们可以期待 mViews, mParamsmRoots 的长度相同,这段代码看起来是正确的。

然而,如果我们把git历史往前推一点,我们会发现 commit 978e7f87:

From: [email protected]
Subject: Fix: WindowManagerGlobal#setStoppedState failed by IOOBE

Symptom:
An application crashed due to IndexOutOfBoundsException.
The exception was thrown at WindowManagerGlobal#setStoppedState.

Root cause:
setStoppedState invokes setWindowStopped for each ViewRoot by
ascending order. If an application removes its view within the
loop, loop index exceeds the number of items.

Solution:
Loop in descending order.

我认为你看到崩溃的设备不包括这个提交,而没有问题的设备包括。

新代码是这样的。

public void setStoppedState(IBinder token, boolean stopped) {
    synchronized (mLock) {
        int count = mViews.size();
        for (int i = count - 1; i >= 0; i--) {
            if (token == null || mParams.get(i).token == token) {
                ViewRootImpl root = mRoots.get(i);
                // Client might remove the view by "stopped" event.
                root.setWindowStopped(stopped);
            }
        }
    }
}

既然你不能影响这些设备 你就得解决这个问题了 提交信息给我们指出了一个明确的方向:确保你没有在这里发生的回调中删除视图。

© www.soinside.com 2019 - 2024. All rights reserved.