Robolectric拿到当前的Activity

在 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:

java 复制代码
// 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 的,比如:

java 复制代码
@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 跳转等),例如:

java 复制代码
// 模拟某个广告点击后启动一个落地页 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(推荐写法):

java 复制代码
@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)(你要捕获它):

java 复制代码
@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 的启动和引用
相关推荐
CYRUS_STUDIO21 小时前
一步步带你移植 FART 到 Android 10,实现自动化脱壳
android·java·逆向
CYRUS_STUDIO21 小时前
FART 主动调用组件深度解析:破解 ART 下函数抽取壳的终极武器
android·java·逆向
蓝倾9761 天前
淘宝/天猫店铺商品搜索API(taobao.item_search_shop)返回值详解
android·大数据·开发语言·python·开放api接口·淘宝开放平台
Propeller1 天前
【Android】LayoutInflater 控件实例化的桥梁类
android
国家二级编程爱好者1 天前
Android开机广播是有序还是无序?广播耗时原因是什么?
android
猿小蔡-Cool1 天前
Robolectric如何启动一个Activity
android·单元测试·rebolectric
Industio_触觉智能1 天前
瑞芯微RK3576开发板Android14三屏异显开发教程
android·开发板·瑞芯微·rk3576·多屏异显·rk3576j·三屏异显
AI视觉网奇1 天前
android adb调试 鸿蒙
android
NRatel1 天前
GooglePlay支付接入记录
android·游戏·unity·支付·googleplay
在下历飞雨1 天前
为啥选了Kuikly?2025“液态玻璃时代“六大跨端框架横向对比
android·harmonyos