如何在Android测试中停止和重新启动活动?

21

我正在尝试编写一个 Android 活动插桩测试,以停止当前活动(onPause(),然后 onStop())并重新启动。我尝试过:

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

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

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


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

getInstrumentation().waitForIdleSync()

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


究竟有什么问题无法正常工作? - yorkw
当你说“手动操作”时,你的意思是什么? - Joel Skrepnek
@JoelSkrepnek,有一个复选框可以启用和禁用该功能。当我取消选中复选框并关闭再重新打开应用程序时,所有数据都会消失。当我选中它并关闭或杀死然后重新打开应用程序时,所有表单数据都会恢复。 - Danilo Bargen
6个回答

15

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

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);

引用自官方开发指南的Activity生命周期图示: 在这里输入图片描述


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

10

我尝试过调用.finish()、 setActivity(null)、getActivity(),它确实重新启动了活动,但对我来说它没有恢复状态。 我在SO上尝试了所有其他答案,并尝试了我能在网上找到的所有其他方法,但它们都对我无效。 经过许多实验,我发现以下内容有效(注:需要API级别11+):

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

当我执行这个操作时,会创建一个新的Activity实例,并且还会创建一个之前已经连接到测试中的Fragment的新实例,同时Activity和Fragment都以预期的方式恢复它们的状态。

我不知道这是如何工作或为什么能工作,我是通过试错达到这个解决方案的,而且我只在运行KitKat系统的Nexus 4上进行了测试。我不能保证它正确模拟了Activity的重建,但对于我的目的而言,它起到了作用。

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

您可以通过查看继承自各种测试用例类的源代码来了解此功能是如何工作的。


2
一个测试生命周期事件的好方法是通过屏幕方向的改变来进行。在我的经验中,这是一种方便的方式,可以使onPause / onStart模式更加稳定。

有趣的想法。问题在于当屏幕方向改变时,Android默认保存表单数据,因此这并不能涵盖这个测试用例... - Danilo Bargen
1
屏幕方向改变会导致完整的Activity生命周期:... -> onPause() -> onStop() -> onDestroy() -> onCreate() -> onStart() -> ...,这与调用activity.finish(); activity = getActivity(); 的行为相同。 - yorkw
@DaniloBargen 我不相信表单数据在活动被销毁时会默认保存。 - Joel Skrepnek
@JoelSkrepnek 我在我的ICS设备上测试了它。杀死或关闭应用程序会导致表单字段为空。更改屏幕方向时,所有表单数据都将保留。 - Danilo Bargen

0

ActivityScenario.recreate() 似乎工作正常。我认为其他复杂的解决方案不再需要了。

鉴于这个测试

@Test
fun activity_is_recreated() {
  activityScenario = ActivityScenario.launch(TestingLifecycleActivity::class.java)

  activityScenario.onActivity {
      Timber.d("inside onActivity $it")        
      //do assertions   
  }

  Timber.d("pre recreate")
  activityScenario.recreate()
  Timber.d("post recreate")

  activityScenario.onActivity {
      Timber.d("inside onActivity $it")
      //do assertions
  }
}

这些是与生命周期相关的日志

Lifecycle status change: TestingLifecycleActivity@e34cfb7 in: PRE_ON_CREATE
Lifecycle status change: TestingLifecycleActivity@e34cfb7 in: CREATED
Lifecycle status change: TestingLifecycleActivity@e34cfb7 in: STARTED
Lifecycle status change: TestingLifecycleActivity@e34cfb7 in: RESUMED
inside onActivity TestingLifecycleActivity@e34cfb7
pre recreate
Schedule relaunch activity: TestingLifecycleActivity
Lifecycle status change: TestingLifecycleActivity@e34cfb7 in: PAUSED
Lifecycle status change: TestingLifecycleActivity@e34cfb7 in: STOPPED
Lifecycle status change: TestingLifecycleActivity@e34cfb7 in: DESTROYED

//new activity instance is launched
Lifecycle status change: TestingLifecycleActivity@ac46813 in: PRE_ON_CREATE
Lifecycle status change: TestingLifecycleActivity@ac46813 in: CREATED
Lifecycle status change: TestingLifecycleActivity@ac46813 in: STARTED
Lifecycle status change: TestingLifecycleActivity@ac46813 in: RESUMED
post recreate
inside onActivity TestingLifecycleActivity@ac46813
Finishing activity: TestingLifecycleActivity@ac46813

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

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接