Robolectric拿到当前的Activity

发布于:2025-08-31 ⋅ 阅读:(23) ⋅ 点赞:(0)

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()拿到 Intent
2. 解析出目标 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 的启动和引用