Appium基础学习之 | Instrumentation再续

26 篇文章 12 订阅
10 篇文章 0 订阅

在《Appium基础学习之 | UiAutomator2.0使用》最后留下了三个问题

1.什么叫做运行器,比如AndroidJUnitRunner、instrumentationTestRunner?

2.UiAutomator2.0基于Instrumentation运行,好像非常复杂,这样做相对于UiAutomator1.0来说有什么优势呢?

3.adb shell am instrument命令的解析
从《Android基础知识学习-Instrumentation启动源码简析》中分析来看,在通过命令adb shell am instrument指定运行器时会取代默认Instrumentation来执行测试,从源码分析来看,默认系统的Instrumentation类中onCreate是一个空方法,而继承Instrumentation的AndroidJUnitRunner类重写了onCreate对将测试的数据做了一些处理,有兴趣的可以继续往下研究下源码看看AndroidJUnitRunner是如何运行并处理数据的。

一、AndroidJUnitRunner、instrumentationTestRunner

Instrumentation这个测试框架的运行器AndroidJUnitRunner与instrumentationTestRunner在执行测试是基于Junit的(注意是执行测试的类继承Junit的TestCase类,而不是说Instrumentation继承Junit

1.AndroidJUnitRunner与instrumentationTestRunner的区别

(1)instrumentationTestRunner运行器是一个旧的运行器,它只支持Junit3,所以如果使用这个运行器,测试方法必须以test开头。

(2)AndroidJUnitRunner运行器是Google推荐使用的,它的出现就是替换instrumentationTestRunner的,它支持所有instrumentationTestRunner的特性以及命令格式,并且有一些新的扩展。

二、Instrumentation

在《Android基础知识学习-Instrumentation启动源码简析》大致简述了Instrumentation的创建过程,Application对象是通过Instrumentation调用callApplicationOnCreate来完成Application的启动;而另外很重要的Activity的onCreate在文中并没有进行源码分析,但也有提到过,在整个Application初始化完成后,会回到ActivityManagerService的attachApplicationLocked方法中通过调用ActivityStackSupervisor的attachApplicationLocked()方法,最后是通过Instrumentation的newActivity、callActivityOnCreate初始化启动主Activity的(这个过程的源码分析就省略了)。然后会调用Activity的attach方法,这个调用是在ActivityThead的performLaunchActivity方法中执行的。

public Instrumentation getInstrumentation()
{
        return mInstrumentation;
}

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.voiceInteractor);             
    }

调用这个attach入参的参数中关注getInstrumentation()这个方法,它返回的是mInstrumentation,这个值是在调用ActivityThread的handleBindApplication中初始化的。然后来到Activity的attach方法

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {
    private static final String TAG = "Activity";
 
    private Instrumentation mInstrumentation;

    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, IVoiceInteractor voiceInteractor) {

......
        mInstrumentation = instr;
    }

}

在Activity类中同样有一个成员变量mInstrumentation,在调用attach后把ActivityThread的mInstrumentation赋值给了Activity的mInstrumentation,后续每一个Activity在启动时经过attach()方法,都会把该mInstrumentation传入Activity赋给Activity.mInstrumentation, 也就是同一个应用进程内,所有Activity共用同一个Instrumentation。

三、UiAutomator2.0基于Instrumentation优缺点

    在android应用UI层的自动化中使用Instrumentation调用Android接口还是非常少见的,使用Instrumentation比较常见的是在Android单元测试。但这并不是说基于Instrumentation就没有任何好处,只要能得到Instrumentation实例,它就可以游刃有余的拥抱应用本身,与Android应用实现无缝接触,就可以使用Android服务及接口,比如得到Context,控制Activity的生命周期、按键操作等等。在这么多好处的情况下,它的缺点很现实,就是不接受跨进程,前面说Instrumentation的时候也应该明白,在整个进程中从ActivityThread实例化Instrumentation后,无论是在Activity还是其他任何地方,都是唯一的Instrumentation。而如果是其他应用进程中,它又有另一个归属于该进程的Instrumentation实例。而UiAutomator2.0除了基于Instrumentation运行外,对于元素的查找与操作,依然支持Accessibility服务,这样就可以完美的弥补Instrumentation不支持跨进程操作的缺陷了。

    另外要提的一点就是使用Instrumentation执行,会把测试代码APK与被测应用APK都推送到设备中运行,这时候测试代码的APK与被测应用APK处于同一进程中,这是基于Instrumentation运行测试的特点,所以它才能完成对被测应用的操作。如果被测应用并不是与UiAutomator2.0测试代码APK不在同一进程,也完全可以使用,因为它支持Accessibility服务对整个android系统事件的监控处理。

四、解决疑惑

在《Android基础知识学习-Instrumentation启动源码简析》的最后得出的结论

用adb shell am instrument命令执行测试,mInstrumentation这个实例指向的并不是默认的Instrumentation而是命令中指定的AndroidJUnitRunner,所以后面调用的是AndroidJUnitRunner的onCreate方法开始执行测试

为了证明这个是对的,上代码

1.被测应用

在Android Studio中新建项目,这个项目需要创建一个Activity,如MainActivity,代码如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Field mInstrumentation;
        Object value = null;
        try {
            Class<?> activityThread = Class.forName("android.app.ActivityThread");
            Method sCurrentActivityThread = activityThread.getMethod("currentActivityThread");
            //获取ActivityThread 对象
            Object activityThreadObject = sCurrentActivityThread.invoke(activityThread, new Object[0]);

            mInstrumentation = activityThread.getDeclaredField("mInstrumentation");
            mInstrumentation.setAccessible(true);
            value = mInstrumentation.get(activityThreadObject);
        }catch (Exception e){
            e.printStackTrace();
        }
        Log.d("debug", "Instrumentation: "+value.getClass().getName());
    }
}

通过反射得到ActivityThread对象,然后得到它的成员变量mInstrumentation,最终log打印出mInstrumentation对象的类名。这个思路应该是很清晰的,因为进程中唯一的Instrumentation就是在ActivityThread中初始化的,所以反射得到ActivityThread对象。

2.测试应用

在项目androidTest目录下会默认创建一个测试类ExampleInstrumentedTest

@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    private Context ct;
    @Before
    public void init(){
        ct = InstrumentationRegistry.getContext();

    }

    @Test
    public void useAppContext() {
        Intent testIntent = ct.getPackageManager().getLaunchIntentForPackage("ymxh.main");
        ct.startActivity(testIntent);
}

通过InstrumentationRegistry得到测试应用的Context,然后启动App默认的Activity。

3.验证

(1)手动运行

把应用打包成APK安装在手机设备中,使用adb与手机设备连接上,然后手动点击运行,在Android Studio的Logcat日志输出窗口中会打印

debug: Instrumentation: android.app.Instrumentation

(2)通过adb shell am instrument命令运行

这里就是运行上面的ExampleInstrumentedTest类,日志打印

debug: Instrumentation: android.support.test.runner.AndroidJUnitRunner

最终验证可以看到,按正常流程使用app启动应用,系统初始化的是默认的Instrumentation;而通过命令执行启动应用,初始化的是指定的运行器。

很多文中说到UiAutomator1.0与UiAutomator2.0的区别都提到说UiAutomator2.0是android应用,需要打包成APK在设备运行;个人觉得这样的说法都是在说它基于Instrumentation的特点,而并不是说UiAutomator2.0本身。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值