在Espresso android中获取当前活动

在Espresso android中获取当前活动,android,ui-automation,android-espresso,Android,Ui Automation,Android Espresso,如果测试跨越多个活动,是否有方法获取当前活动 getActivtiy()方法只提供一个用于启动测试的活动 我试过下面的方法 public Activity getCurrentActivity() { Activity activity = null; ActivityManager am = (ActivityManager) this.getActivity().getSystemService(Context.ACTIVITY_SERVICE); List<A

如果测试跨越多个活动,是否有方法获取当前活动

getActivtiy()方法只提供一个用于启动测试的活动

我试过下面的方法

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;
}
公共活动getCurrentActivity(){ 活动=空; ActivityManager am=(ActivityManager)this.getActivity().getSystemService(Context.ACTIVITY_服务); List taskInfo=am.getRunningTasks(1); 试一试{ 类myClass=taskInfo.get(0.topActivity.getClass(); 活动=(活动)myClass.newInstance(); } 捕获(例外e){ } 返回活动; }
但是我得到了空对象。

在浓缩咖啡中,您可以使用
ActivityLifecycleMonitorRegistry
,但它没有官方支持,因此在未来的版本中可能无法使用

以下是它的工作原理:

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];
}
Activity getCurrentActivity()抛出可丢弃的{
getInstrumentation().waitForIdleSync();
最终活动[]活动=新活动[1];
runTestOnUiThread(新的Runnable(){
@凌驾
公开募捐{
java.util.Collection activities=ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.Resume);
活动[0]=Iterables.getOnlyElement(活动);
}});
返回活动[0];
}

如果您只需对照当前的
活动进行检查,请使用may get God with native Espresso one liner检查是否启动了预期目的:

intended(hasComponent(new ComponentName(getTargetContext(), ExpectedActivity.class)));
// 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));
如果意式浓缩咖啡与你的不匹配,它也会同时向你展示你的意图

您需要的唯一设置是在测试中用
IntentsTestRule
替换
ActivityTestRule
,以便跟踪intent的启动。并确保此库位于您的
build.gradle
依赖项中:

androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
公共静态活动getActivity(){
最终活动[]当前活动=新活动[1];
Espresso.onView(AllOf.AllOf(ViewMatchers.withId(android.R.id.content),isDisplayed()).perform(newviewaction()){
@凌驾
公共匹配器getConstraints(){
返回isAssignableFrom(View.class);
}
@凌驾
公共字符串getDescription(){
返回“从文本视图获取文本”;
}
@凌驾
公共作废执行(UiController UiController,视图){
if(view.getContext()活动实例){
Activity activity1=((Activity)view.getContext());
currentActivity[0]=activity1;
}
}
});
返回当前活动[0];
}

我喜欢@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];
}
请注意,但在Firebase测试实验室中运行测试时,这将不起作用。这会导致失败

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

如果测试用例中只有一个活动,则可以执行以下操作:

1.声明您测试
规则

那是一块馅饼

在许多浓缩咖啡测试中,公认的答案可能不起作用。以下内容适用于在API 25设备上运行的espresso 2.2.2版和Android compile/target 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;
}

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

我甚至尝试了
阶段。PRE_ON_CREATE
仍然没有得到任何活动

注意:我无法使用
ActivityTestRule
intettestrule
,因为我正在使用
activitiy别名启动我的活动,当我想测试别名是否工作时,在测试中使用实际类没有任何意义

我的解决方案是通过
ActivityLifecycleMonitorRegistry
订阅生命周期更改,并在启动活动之前阻止测试线程:

intended(hasComponent(new ComponentName(getTargetContext(), ExpectedActivity.class)));
// 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));
//注意:确保这是一个强引用(作为类字段上移),否则将被GCed,您将无法稳定地接收更新。
ActivityLifecycleCallback lifeCycleCallback=新建ActivityLifecycleCallback(){
@凌驾
ActivityLifeCycleChanged上的公共无效(活动、阶段){
setValue(((MyActivity)activity.getClass());
//释放测试线程
锁。倒计时();
}
};
//用于在启动活动之前阻止测试线程
最终倒计时闩锁=新倒计时闩锁(1);

最终持有者我无法获得任何其他解决方案,因此我不得不这样做:

声明您的
活动测试规则

@Rule
public ActivityTestRule<MainActivity> mainActivityTestRule =
        new ActivityTestRule<>(MainActivity.class);
添加要向应用程序上下文注册以获取生命周期更新的帮助器方法:

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) { }
            });
}
添加助手方法以获取当前活动

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

因此,一旦您启动了第一个活动,只需调用
monitorCurrentActivity()
,然后每当您需要当前活动的引用时,只需调用
getCurrentActivity()
我改进了@Fabian Streitel answer,这样您就可以使用此方法而无需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];
}

Android团队已将ActivityTestRule
替换为。我们可以使用
activityTestRule
执行
activityTestRule.getActivity()
,但不能使用。以下是我的解决方案,用于从中获取
活动
(灵感来自@Ryan和@Fabian solutions)

以下是用于访问当前活动的通用util的Kotlin版本:

class CurrentActivityDelegate(application: Application) {
    private var cachedActivity: Activity? = null

    init {
        monitorCurrentActivity(application)
    }

    fun getCurrentActivity() = cachedActivity

    private fun monitorCurrentActivity(application: Application) {
        application.registerActivityLifecycleCallbacks(
            object : Application.ActivityLifecycleCallbacks {
                override fun onActivityResumed(activity: Activity) {
                    cachedActivity = activity
                    Log.i(TAG, "Current activity updated: ${activity::class.simpleName}")
                }

                override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {}
                override fun onActivityStarted(activity: Activity?) {}
                override fun onActivityPaused(activity: Activity?) {}
                override fun onActivityStopped(activity: Activity?) {}
                override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
                override fun onActivityDestroyed(activity: Activity?) {}
            })
    }
}
然后像这样简单地使用:

@Before
fun setup() {
    currentActivityDelegate = CurrentActivityDelegate(activityTestRule.activity.application)
}

对espresso进行更新后,是否仍有可能为空?我目前根本无法从中获取非空值,但我曾经能够。将此代码用于espresso 2.0在Kotlin中有一个类似的解决方案:这种方法与AndroidX测试相关吗?您是否设法找到了Firebase测试实验室失败的原因?我不清楚他们为什么要这样做,但该视图并没有将活动作为其上下文,而是将其作为装饰
@get:Rule
var activityRule = ActivityScenarioRule(MainActivity::class.java)
...
private fun getActivity(): Activity? {
  var activity: Activity? = null
  activityRule.scenario.onActivity {
    activity = it
  }
  return activity
}
class CurrentActivityDelegate(application: Application) {
    private var cachedActivity: Activity? = null

    init {
        monitorCurrentActivity(application)
    }

    fun getCurrentActivity() = cachedActivity

    private fun monitorCurrentActivity(application: Application) {
        application.registerActivityLifecycleCallbacks(
            object : Application.ActivityLifecycleCallbacks {
                override fun onActivityResumed(activity: Activity) {
                    cachedActivity = activity
                    Log.i(TAG, "Current activity updated: ${activity::class.simpleName}")
                }

                override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {}
                override fun onActivityStarted(activity: Activity?) {}
                override fun onActivityPaused(activity: Activity?) {}
                override fun onActivityStopped(activity: Activity?) {}
                override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
                override fun onActivityDestroyed(activity: Activity?) {}
            })
    }
}
@Before
fun setup() {
    currentActivityDelegate = CurrentActivityDelegate(activityTestRule.activity.application)
}