在 Espresso android 中获取当前活动

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24517291/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 08:19:36  来源:igfitidea点击:

Get Current Activity in Espresso android

androidui-automationandroid-espresso

提问by fenrigne123

In case of a test that crosses multiple activities, is there a way to get current activity?

如果测试跨越多个活动,有没有办法获取当前活动?

getActivtiy() method just gives one activity that was used to start the test.

getActivtiy() 方法只给出一个用于启动测试的活动。

I tried something like below,

我试过类似下面的东西,

public Activity getCurrentActivity() {
    Activity activity = null;
    ActivityManager am = (ActivityManager) this.getActivity().getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
    try {
        Class<?> myClass = taskInfo.get(0).topActivity.getClass();
        activity = (Activity) myClass.newInstance();
    }
    catch (Exception e) {

    }
    return activity;
}

but I get null object.

但我得到空对象。

采纳答案by lacton

In Espresso, you can use ActivityLifecycleMonitorRegistrybut it is not officially supported, so it may not work in future versions.

在 Espresso 中,您可以使用,ActivityLifecycleMonitorRegistry但未得到官方支持,因此在未来版本中可能无法使用。

Here is how it works:

下面是它的工作原理:

Activity getCurrentActivity() throws Throwable {
  getInstrumentation().waitForIdleSync();
  final Activity[] activity = new Activity[1];
  runTestOnUiThread(new Runnable() {
    @Override
    public void run() {
      java.util.Collection<Activity> activities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
      activity[0] = Iterables.getOnlyElement(activities);
  }});
  return activity[0];
}

回答by riwnodennyk

If all you need is to make the check against current Activity, use may get along with native Espresso one-liner to check that expected intent was launched:

如果您只需要根据 current 进行检查Activity,则可以使用原生 Espresso one-liner 来检查是否启动了预期的意图:

intended(hasComponent(new ComponentName(getTargetContext(), ExpectedActivity.class)));

Espresso will also show you the intents fired in the meanwhile if not matching yours.

如果与您的意图不匹配,Espresso 还会向您显示同时激发的意图。

The only setup you need is to replace ActivityTestRulewith IntentsTestRulein the test to let it keep track of the intents launching. And make sure this library is in your build.gradledependencies:

您需要的唯一设置是在测试中替换ActivityTestRuleIntentsTestRule,让它跟踪启动的意图。并确保此库在您的build.gradle依赖项中:

androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'

回答by Fabian Streitel

I like @Ryan's version as it doesn't use undocumented internals, but you can write this even shorter:

我喜欢@Ryan 的版本,因为它不使用未记录的内部结构,但您可以将其写得更短:

private Activity getCurrentActivity() {
    final Activity[] activity = new Activity[1];
    onView(isRoot()).check(new ViewAssertion() {
        @Override
        public void check(View view, NoMatchingViewException noViewFoundException) {
            activity[0] = (Activity) view.getContext();
        }
    });
    return activity[0];
}

Please be aware, though that this will not work when running your tests in Firebase Test Lab. That fails with

请注意,这在 Firebase 测试实验室中运行测试时不起作用。那失败了

java.lang.ClassCastException: com.android.internal.policy.DecorContext cannot be cast to android.app.Activity

回答by Tanin

The Android team has replaced ActivityTestRulewith ActivityScenario. We could do activityTestRule.getActivity()with ActivityTestRulebut not with ActivityScenario. Here is my work around solution for getting an Activityfrom ActivityScenario(inspired by @Ryan and @Fabian solutions)

Android 团队已替换ActivityTestRuleActivityScenario。我们可以activityTestRule.getActivity()使用ActivityTestRule但不能使用ActivityScenario。这是我ActivityActivityScenario获取一个解决方案的工作(灵感来自@Ryan 和@Fabian 解决方案)

@get:Rule
var activityRule = ActivityScenarioRule(MainActivity::class.java)
...
private fun getActivity(): Activity? {
  var activity: Activity? = null
  activityRule.scenario.onActivity {
    activity = it
  }
  return activity
}

回答by William Brawner

I couldn't get any of the other solutions to work, so I ended up having to do this:

我无法让任何其他解决方案工作,所以我最终不得不这样做:

Declare your ActivityTestRule:

声明你的ActivityTestRule

@Rule
public ActivityTestRule<MainActivity> mainActivityTestRule =
        new ActivityTestRule<>(MainActivity.class);

Declare a finalActivity array to store your activities:

声明一个finalActivity 数组来存储您的活动:

private final Activity[] currentActivity = new Activity[1];

Add a helper method to register with the application context to get lifecycle updates:

添加一个辅助方法来注册应用程序上下文以获取生命周期更新:

private void monitorCurrentActivity() {
    mainActivityTestRule.getActivity().getApplication()
            .registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                @Override
                public void onActivityCreated(final Activity activity, final Bundle savedInstanceState) { }

                @Override
                public void onActivityStarted(final Activity activity) { }

                @Override
                public void onActivityResumed(final Activity activity) {
                    currentActivity[0] = activity;
                }

                @Override
                public void onActivityPaused(final Activity activity) { }

                @Override
                public void onActivityStopped(final Activity activity) { }

                @Override
                public void onActivitySaveInstanceState(final Activity activity, final Bundle outState) { }

                @Override
                public void onActivityDestroyed(final Activity activity) { }
            });
}

Add a helper method to get the current activity

添加辅助方法以获取当前活动

private Activity getCurrentActivity() {
    return currentActivity[0];
}

So, once you've launched your first activity, just call monitorCurrentActivity()and then whenever you need a reference to the current activity you just call getCurrentActivity()

所以,一旦你启动了你的第一个活动,只要调用monitorCurrentActivity(),然后只要你需要对当前活动的引用,你就调用getCurrentActivity()

回答by Ryan

public static Activity getActivity() {
    final Activity[] currentActivity = new Activity[1];
    Espresso.onView(AllOf.allOf(ViewMatchers.withId(android.R.id.content), isDisplayed())).perform(new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isAssignableFrom(View.class);
        }

        @Override
        public String getDescription() {
            return "getting text from a TextView";
        }

        @Override
        public void perform(UiController uiController, View view) {
            if (view.getContext() instanceof Activity) {
                Activity activity1 = ((Activity)view.getContext());
                currentActivity[0] = activity1;
            }
        }
    });
    return currentActivity[0];
}

回答by Slava

If you have the only Activity in your test case, you can do:

如果您的测试用例中只有 Activity,则可以执行以下操作:

1. declare you test Rule

1.声明你测试 Rule

@Rule
public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(TestActivity.class);

2. get you Activity:

2.让你Activity

mActivityTestRule.getActivity()

That's a piece of pie!

那是一块馅饼!

回答by Artem M

I improved @Fabian Streitel answer so you can use this method without ClassCastException

我改进了@Fabian Streitel 的答案,因此您可以在没有 ClassCastException 的情况下使用此方法

public static Activity getCurrentActivity() {
    final Activity[] activity = new Activity[1];

    onView(isRoot()).check((view, noViewFoundException) -> {

        View checkedView = view;

        while (checkedView instanceof ViewGroup && ((ViewGroup) checkedView).getChildCount() > 0) {

            checkedView = ((ViewGroup) checkedView).getChildAt(0);

            if (checkedView.getContext() instanceof Activity) {
                activity[0] = (Activity) checkedView.getContext();
                return;
            }
        }
    });
    return activity[0];
}

回答by Bakhshi

Solution proposed by @lacton didn't work for me, probably because activity was not in a state that was reported by ActivityLifecycleMonitorRegistry.

@lacton 提出的解决方案对我不起作用,可能是因为活动未处于ActivityLifecycleMonitorRegistry.

I even tried Stage.PRE_ON_CREATEstill didn't get any activity.

我什至尝试过Stage.PRE_ON_CREATE仍然没有任何活动。

Note: I could not use the ActivityTestRuleor IntentTestRulebecause I was starting my activity using activitiy-aliasand didn't make any sense to use the actual class in the tests when I want to test to see if the alias works.

注意:我无法使用ActivityTestRuleorIntentTestRule因为我开始我的活动使用activitiy-alias并且当我想测试别名是否有效时在测试中使用实际类没有任何意义。

My solution to this was subscribing to lifecycle changes through ActivityLifecycleMonitorRegistryand blocking the test thread until activity is launched:

我对此的解决方案是通过订阅生命周期更改ActivityLifecycleMonitorRegistry并阻止测试线程直到活动启动:

// NOTE: make sure this is a strong reference (move up as a class field) otherwise will be GCed and you will not stably receive updates.
ActivityLifecycleCallback lifeCycleCallback = new ActivityLifecycleCallback() {
            @Override
            public void onActivityLifecycleChanged(Activity activity, Stage stage) {
                classHolder.setValue(((MyActivity) activity).getClass());

                // release the test thread
                lock.countDown();
            }
         };

// used to block the test thread until activity is launched
final CountDownLatch lock = new CountDownLatch(1);
final Holder<Class<? extends MyActivity>> classHolder = new Holder<>();
instrumentation.runOnMainSync(new Runnable() {
   @Override
    public void run() {
        ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(lifeCycleCallback);
     }
});

// start the Activity
intent.setClassName(context, MyApp.class.getPackage().getName() + ".MyActivityAlias");
context.startActivity(intent);
// wait for activity to start
lock.await();

// continue with the tests
assertTrue(classHolder.hasValue());
assertTrue(classHolder.getValue().isAssignableFrom(MyActivity.class));

Holderis basically a wrapper object. You can use an array or anything else to capture a value inside the anonymous class.

Holder基本上是一个包装对象。您可以使用数组或其他任何东西来捕获匿名类中的值。

回答by pbm

The accepted answer may not work in many espresso tests. The following works with espresso version 2.2.2 and Android compile/target SDK 27 running on API 25 devices:

接受的答案可能不适用于许多浓缩咖啡测试。以下适用于在 API 25 设备上运行的 espresso 版本 2.2.2 和 Android 编译/目标 SDK 27:

@Nullable
private Activity getActivity() {
    Activity currentActivity = null;

    Collection resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(RESUMED);
    if (resumedActivities.iterator().hasNext()){
        currentActivity = (Activity) resumedActivities.iterator().next();
    }
    return currentActivity;
}