【问题标题】:How to stop and restart an activity in an android instrumentation test?如何在 android 仪器测试中停止和重新启动活动?
【发布时间】:2012-04-24 10:26:16
【问题描述】:

我正在尝试编写一个停止(onPause(),然后是onStop())并重新启动当前活动的 Android 活动检测测试。我试过了

activity.finish();
activity = getActivity();

...但这似乎无法正常工作。

测试的目标是断言表单数据在onPause() 方法期间存储并在onStart() 方法期间重新读取。它在手动执行时有效,但测试失败,由此我得出结论,activity.finish() 似乎是停止和重新启动活动的错误方法。


编辑:我的主要问题似乎是同步问题。重新启动活动后,测试运行程序没有等待所有事件处理程序完成。以下行暂停测试执行,直到活动空闲:

getInstrumentation().waitForIdleSync()

除此之外,请查看已接受的答案,以获取有关生命周期的更多有价值信息。

【问题讨论】:

  • 究竟是什么似乎无法正常工作?
  • @yorkw 我更新了问题,感谢您的评论。
  • 您说“手动操作”是什么意思?
  • @JoelSkrepnek 有一个复选框可以启用和禁用该功能。当我取消选中复选框并关闭并重新打开应用程序时,所有数据都消失了。当我检查它并关闭或杀死然后重新打开应用程序时,所有表单数据都会恢复。

标签: android testing android-testing


【解决方案1】:

通过调用(或触发屏幕方向更改):

activity.finish(); // old activity instance is destroyed and shut down.
activity = getActivity(); // new activity instance is launched and created.

使活动经历完整的娱乐生命周期:

onPause() -> onStop() -> onDestroy() -> onCreate()

你需要的是:

onPause() -> onStop() -> onRestart()

我最近曝光了Instrumentation API,发现了很多有趣的活动生命周期触发方法callActivityOnXXX(),下面一行代码应该可以解决问题:

MyActivity myActivity = getActivity();
// make activity falling into restart phase:
getInstrumentation().callActivityOnRestart(myActivity);

引用官方开发指南的活动生命周期图:

【讨论】:

  • 实际上,我已经尝试了第一个解决方案,但正如您在我的问题中看到的那样,它似乎不起作用。但也许你是对的,问题是无关的......关于你的第二个建议,我稍后会尝试那个......
  • 确实做到了,非常感谢! :) 你介意把它放在你的答案的顶部,让其他人更容易找到吗?
  • 感谢您的编辑。最后一句话:我也遇到了同步问题。测试没有等待我的生命周期事件处理程序完成。重新启动活动后添加getInstrumentation().waitForIdleSync() 有帮助。
  • getInstrumentation().callActivityOnRestart(myActivity) 真的会导致 onPause() -> onStop() -> onRestart() 吗?我试过了,不行。
  • @siik,没有更多细节和调查很难说。我的第一枪是确保 callActivityOnRestart() 在应用程序的主线程(或 UI 线程)中被调用,即如果使用 @UiThreadTest 注释包装整个测试方法体。
【解决方案2】:

我尝试调用 .finish()、setActivity(null)、getActivity() 并且它确实重新启动了活动,但对我来说它并没有恢复状态。我尝试了关于 SO 的所有其他答案,以及我可以在网上找到的所有其他方法,但没有一个对我有用。经过大量实验,我发现了以下作品(nb:需要 API 级别 11+):

    getInstrumentation().runOnMainSync(new Runnable() {
        @Override
        public void run() {
            activity.recreate();
        }
    });
    setActivity(null);
    activity = getActivity();

当我这样做时,会创建一个新的 Activity 实例,并且还会创建一个我在测试早期附加到该 Activity 的片段的新实例,并且该 Activity 和片段都以预期的方式恢复它们的状态。

我不知道这是如何工作的,也不知道为什么会这样,我通过反复试验得到了这个解决方案,而且我只在运行 KitKat 的 Nexus 4 上对其进行了测试。我不能保证它正确地模拟了一个活动游戏,但它可以满足我的目的。

编辑:后来我弄清楚了它是如何工作的。 getActivity() 通过注册接收正在创建的新活动的钩子来工作,这些钩子捕获由 activity.recreate() 创建的新活动。需要setActivity(null) 清除支持getActivity 的内部缓存,否则它将返回旧的而不是寻找新的。

您可以通过检查一个扩展的各种测试用例类的源代码来了解其工作原理。

【讨论】:

    【解决方案3】:

    测试生命周期事件的一个好方法是通过屏幕方向更改。以我的经验,这是一种对 onPause / onStart 模式进行防爆的便捷方式。

    【讨论】:

    • 有趣的想法。这样做的问题是,Android 在更改屏幕方向时默认保存表单数据,所以这并没有真正涵盖这个测试用例......
    • 屏幕方向更改导致完整的 Activity 生命周期:... -> onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> .. .,与调用activity.finish()的行为相同;活动 = getActivity();
    • @DaniloBargen 我不相信在销毁活动时默认保存表单数据。
    • @JoelSkrepnek 我在我的 ICS 设备上对其进行了测试。杀死或关闭应用程序会导致一个空的表单字段。更改屏幕方向时,所有表单数据都会保留。
    【解决方案4】:

    也许你可以尝试保存你的活动名称, 完成它...并使用反射来获取 .class 的新实例以创建新意图...

    【讨论】:

      【解决方案5】:

      如下修改你的代码:

      mActivity.finish();
          setActivity(null);
          mActivity = this.getActivity();
      

      A full explanation can be found in this question

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-15
        相关资源
        最近更新 更多