最近遇到一个应用崩溃问题,这个问题是由于在 Activity 的 onSaveInstanceState 中进行了数据的保存,然后在 onRestoreInstanceState 进行解析的时候出现崩溃。
实际测试的时候,发现当内存充足的时候,非常难稳定的诱发 Activity 的 onSaveInstanceState 事件。
早期的版本,可以通过 ActivityManagerNative.getDefault().setAlwaysFinish 来强制系统在 Activity 切换到后台之后,立即触发 onSaveInstanceState 。
参考代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// Updates the system Always Finish setting private void writeFinishOptions() { try { // Due to restrictions related to hidden APIs, need to emulate the line below // using reflection: // ActivityManagerNative.getDefault().setAlwaysFinish(mAlwaysFinish); final Class classActivityManagerNative = Class.forName("android.app.ActivityManagerNative"); final Method methodGetDefault = classActivityManagerNative.getMethod("getDefault"); final Method methodSetAlwaysFinish = classActivityManagerNative.getMethod("setAlwaysFinish", new Class[] {boolean.class}); final Object objectInstance = methodGetDefault.invoke(null); methodSetAlwaysFinish.invoke(objectInstance, new Object[]{mAlwaysFinish}); } catch (Exception ex) { showAlert("Could not set always finish:\n\n" + ex, "Error"); } } // Gets the latest AlwaysFinish value from the system and // updates the checkbox private void updateFinishOptions() { mAlwaysFinish = Settings.System.getInt(getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0) != 0; mAlwaysFinishCB.setChecked(mAlwaysFinish); } |
但是遗憾的是,新系统比如 Android 8 等系统上,在真机环境中已经没办法通过上述的方法进行诱发了。系统会直接抛出异常,或者设置无效。
真机环境,可以尝试在 开发人员选项 中设置开启 “不保留活动” 按钮,如下图所示:
对于编写单元测试用例的情况来说,建议使用 AndroidX 引入的 ActivityScenario 来进行测试,参考代码如下;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private boolean isEmulator() { final Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); return ((TelephonyManager) appContext.getSystemService(Context.TELEPHONY_SERVICE)) .getNetworkOperatorName().toLowerCase().equals("android"); } // 此测试用例仅仅建议在Android模拟器中运行,真机调试的时候会在此代码中卡住 if (isEmulator()) { final ActivityScenario<SecKeyboardActivity> scenario = ActivityScenario.launch(Activity.class); scenario.moveToState(Lifecycle.State.CREATED); // Moves the activity state to State.CREATED. // 诱发系统调用 onSaveInstanceState/onRestoreInstanceState scenario.recreate(); } |
目前测试发现,在真机上执行上述测试用例(Android 10),会在 ActivityScenario.launch(Activity.class); 的时候抛出超时异常,目前真机暂时没解决方法,建议在模拟器上执行测试用例。
可能的崩溃日志如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
10:54:42 V/InstrumentationResultParser: java.lang.AssertionError: Activity never becomes requested state "[RESUMED]" (last lifecycle transition = "STOPPED") 10:54:42 V/InstrumentationResultParser: at androidx.test.core.app.ActivityScenario.waitForActivityToBecomeAnyOf(ActivityScenario.java:228) 10:54:42 V/InstrumentationResultParser: at androidx.test.core.app.ActivityScenario.moveToState(ActivityScenario.java:368) 10:54:42 V/InstrumentationResultParser: at com.myapplication.android.test.HomeTest.launchActivity(HomeTest.java:30) 10:54:42 V/InstrumentationResultParser: at java.lang.reflect.Method.invoke(Native Method) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) 10:54:42 V/InstrumentationResultParser: at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) 10:54:42 V/InstrumentationResultParser: at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:76) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 10:54:42 V/InstrumentationResultParser: at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.Suite.runChild(Suite.java:128) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.Suite.runChild(Suite.java:27) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 10:54:42 V/InstrumentationResultParser: at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 10:54:42 V/InstrumentationResultParser: at org.junit.runner.JUnitCore.run(JUnitCore.java:137) 10:54:42 V/InstrumentationResultParser: at org.junit.runner.JUnitCore.run(JUnitCore.java:115) 10:54:42 V/InstrumentationResultParser: at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56) 10:54:42 V/InstrumentationResultParser: at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:388) 10:54:42 V/InstrumentationResultParser: at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2075) |
参考链接
- How to test onSaveInstanceState and onRestoreInstanceState on a real device
- Activity lifecycle unit testing
- 测试应用的 Activity
- AndroidX.Test ActivityScenario: java.lang.AssertionError: Activity never becomes requested state “[RESUMED]” (last lifecycle transition = “STOPPED”)
- Create DummyActivity inside androidTest folder for testing
- Android Studio 3.6.3/4.0配置Robolectric-4.3.1,Powermock-1.6.6单元测试环境
- How do you force a configuration change in an Android Robolectric test?
- Fragment 深度解析_4 : onSaveInstanceState方法回调时机
- How do you force a configuration change in an Android Robolectric test?
- How to test code built to save/restore Lifecycle of an Activity?