在 Robolectric 测试中,获取当前正在显示 / 处于前台的 Activity 是一个常见的需求,尤其是在以下场景中:
- 你启动了一个新的 Activity(比如通过 Intent 跳转、广告落地页、DeepLink 等),
- 你想验证这个 Activity 是否正确启动,
- 或者你想对这个 当前 Activity 做进一步的 UI 操作、断言等。
❓ 问题:如何在 Robolectric 中获取“当前” Activity?
在 Android 真实系统中,我们可能通过一些方式(比如 ActivityLifecycleCallbacks 或 Application 的 activity 栈)获取当前 Activity,但在 Robolectric 测试环境 中,没有真正的系统 Activity 栈管理。
不过,Robolectric 提供了一个非常简单且可靠的方法:
✅ 通过 **
ShadowApplication
**获取最近启动的 Activity(即当前正在运行的 Activity)
✅ 正确方法:使用 ShadowApplication
获取最新的 Activity
Robolectric 提供了 ShadowApplication
,它可以追踪通过 startActivity(Intent)
启动的 Activity,因此:
✅ 你可以这样获取 最近启动的(也就是当前显示的)Activity:
// 1. 获取当前 Application 的 Shadow
ShadowApplication shadowApplication = Shadows.shadowOf(RuntimeEnvironment.getApplication());
// 2. 获取最近启动的 Intent(即通过 startActivity 启动的)
Intent startedIntent = shadowApplication.getNextStartedActivity();
// 3. 如果你只是想获取这个 Intent 对应的 Activity 类,可以这样:
if (startedIntent != null) {
Class<?> startedActivityClass = startedIntent.getComponent().getClassName();
System.out.println("启动的 Activity 类名: " + startedActivityClass);
}
// 4. 如果你想真正获取这个 "当前" Activity 的实例,你有两个方式:
// 方式一:如果你已经通过 Robolectric.buildActivity(...) 启动它,那直接使用返回的实例即可
// 比如:
// MyActivity activity = Robolectric.buildActivity(MyActivity.class).create().get();
// 这个 activity 就是你当前的测试对象
// 方式二:如果你是通过 startActivity(Intent) 启动的(比如广告跳转、页面跳转等),
// 那么 Robolectric **并不会自动创建 Activity 实例**,它只是记录了 Intent。
// 所以,你如果想拿到这个 "被启动的 Activity 实例",需要手动用 Robolectric 构建它,如下:
if (startedIntent != null) {
String activityClassName = startedIntent.getComponent().getClassName();
try {
Class<?> activityClass = Class.forName(activityClassName);
if (Activity.class.isAssignableFrom(activityClass)) {
@SuppressWarnings("unchecked")
Class<? extends Activity> activityClazz = (Class<? extends Activity>) activityClass;
// 模拟构建并“启动”这个 Activity(你可以传入 Intent)
Activity launchedActivity = Robolectric.buildActivity(activityClazz, startedIntent)
.create()
.start()
.resume()
.get();
// ✅ 这个 launchedActivity 就是你想拿的“当前” Activity
System.out.println("模拟启动的当前 Activity: " + launchedActivity.getClass().getSimpleName());
// 你可以对这个 Activity 做断言或 UI 操作
// 例如:TextView tv = launchedActivity.findViewById(R.id.xxx);
}
} catch (ClassNotFoundException e) {
fail("无法加载被启动的 Activity 类: " + activityClassName);
}
}
✅ 更常见的情况:你手动启动了 Activity,直接用它的返回值
在 绝大多数 Robolectric 单元测试中,你其实 是直接通过 **Robolectric.buildActivity(...)
**来启动 Activity 的,比如:
@Test
public void testMyActivity() {
// 🟢 这是你启动 Activity 的地方
MyActivity activity = Robolectric.buildActivity(MyActivity.class)
.create()
.start()
.resume()
.get();
// ✅ 那么这个 `activity` 就是当前你启动的 Activity,你可以直接使用它!
assertNotNull(activity);
TextView textView = activity.findViewById(R.id.text_view);
assertEquals("Hello Robolectric", textView.getText().toString());
}
🔍 在这种情况下,“当前 Activity” 就是你通过 **.get()
**拿到的这个 activity 对象。
✅ 所以,如果你是自己通过 Robolectric 启动的 Activity,那么你根本不需要去“获取”当前 Activity,因为你已经有了它的引用!
✅ 特殊情况:通过 startActivity(Intent) 启动的 Activity(比如模拟跳转)
如果你在测试中 调用了某个对象的 startActivity(Intent) (比如你的广告模块、点击事件、Router 跳转等),例如:
// 模拟某个广告点击后启动一个落地页 Activity
Intent intent = new Intent(ApplicationProvider.getApplicationContext(), AdDetailActivity.class);
someObject.startActivity(intent); // 例如:context.startActivity(intent);
那么:
- Robolectric 不会自动实例化 AdDetailActivity,
- 但它会 记录你调用了 startActivity(intent) ,
- 你可以用
ShadowApplication.getNextStartedActivity()
拿到这个 Intent, - 然后 手动用 Robolectric 构建并启动这个 Activity,从而拿到它的实例(即你想要的“当前” Activity)。
✅ 总结:如何在 Robolectric 中获取“当前 Activity”
场景 | 说明 | 如何获取“当前 Activity” |
---|---|---|
✅ 你手动用 Robolectric 启动了 Activity(推荐方式) 比如: Robolectric.buildActivity(MyActivity.class)...get() |
你完全掌握 Activity 的创建过程 | 直接通过 **.get() **拿到的对象就是当前 Activity |
🧩 你通过某个代码逻辑(如点击、路由、广告 SDK)调用了 startActivity(Intent) | Robolectric 记录了 Intent,但不会自动创建 Activity 实例 | 1. 用ShadowApplication.getNextStartedActivity() 拿到 Intent2. 解析出目标 Activity 类名 3. 用 Class.forName() +Robolectric.buildActivity(...) 手动构建并启动它4. 最终 .get() 就是你想拿的“当前” Activity |
❌ 你试图获取系统当前栈顶的 Activity(像在真机那样) | Robolectric 没有真实的 Activity 栈管理,不能像ActivityManager 那样获取 |
Robolectric 不提供直接获取“当前正在显示的 Activity”的 API,你必须自己管理或跟踪 |
✅ 推荐的最佳实践
✅ 如果是你自己启动的 Activity(推荐写法):
@Test
public void testActivityIsLaunchedAndVisible() {
// 1. 你自己启动 Activity
MyActivity activity = Robolectric.buildActivity(MyActivity.class)
.create()
.start()
.resume()
.get();
// 2. 这个 activity 就是“当前”的 Activity,直接使用即可
assertNotNull(activity);
// 3. 做 UI 测试、断言等
TextView tv = activity.findViewById(R.id.text_view);
assertEquals("Hello", tv.getText().toString());
}
👉 这个 **activity
**就是你当前测试的、处于 resumed 状态的 Activity,也就是你想要的“当前 Activity”。
✅ 如果是其他代码调用了 startActivity(Intent)(你要捕获它):
@Test
public void testAdClickLaunchesAdActivity() {
// 1. 模拟点击广告,内部会调用 startActivity(intent)
expressAd.show(hostActivity, null);
// 2. 捕获启动的 Intent
Intent startedIntent = Shadows.shadowOf(RuntimeEnvironment.getApplication()).getNextStartedActivity();
assertNotNull(startedIntent);
// 3. 拿到目标 Activity 类名
String className = startedIntent.getComponent().getClassName();
Class<?> activityClass;
try {
activityClass = Class.forName(className);
} catch (ClassNotFoundException e) {
fail("找不到启动的 Activity 类: " + className);
return;
}
// 4. 确保是 Activity
if (!Activity.class.isAssignableFrom(activityClass)) {
fail("启动的不是 Activity: " + className);
return;
}
// 5. 手动构建并“启动”这个 Activity
@SuppressWarnings("unchecked")
Class<? extends Activity> activityClazz = (Class<? extends Activity>) activityClass;
Activity launchedActivity = Robolectric.buildActivity(activityClazz, startedIntent)
.create()
.start()
.resume()
.get();
// 6. 这个 launchedActivity 就是你想拿的“当前” Activity
assertNotNull(launchedActivity);
}
❗ Robolectric 没有直接获取“当前 Activity”的 API
⚠️ 注意:Robolectric 不提供类似于 **
getCurrentActivity()
**或 **getTopActivity()
**这样的方法!原因是 Robolectric 没有维护一个真实的系统级 Activity 栈,它只是模拟了单个 Activity 的生命周期和行为。
所以如果你想获取“当前” Activity,你必须 自己启动它并持有引用,或者 通过 Intent 捕获并手动构建它。
✅ 总结一句话
你想获取的“当前 Activity”是怎么来的? | 你应该怎么做? |
---|---|
是你自己通过Robolectric.buildActivity(...).get() 启动的 |
直接使用.get() 返回的 Activity 对象,它就是“当前”的 |
是通过某个代码(如广告、点击事件)调用了startActivity(intent) |
用ShadowApplication.getNextStartedActivity() 捕获 Intent,再通过Class.forName + Robolectric.buildActivity(...) 手动构建并启动,.get() 就是你要的 Activity |
你希望有现成的 API 如getCurrentActivity() |
Robolectric 没有提供,你必须自己管理 Activity 的启动和引用 |