Android之UiAutomator测试框架源码分析(第四篇:UiDevice对象的创建过程分析)

(注意:本文基于UI Automator测试框架版本为2.2.0)

UiDevice简介

    创建的UiDevice对象代表1个Android设备,可能是手机、电视、手表、车载设备等等,凡是安装Android系统的都属于Android设备……当然所谓的代表也是抽象的现实世界。

    UiDevice的构造方法是包内访问权限(default修饰),UiDevice类位于androidx.test.uiautomator包中,与我们自己写的测试代码不在同一个包中,所以我们不能通过正常方式调用UiDevice的构造方法创建UiDevice对象,无法使用new的去创建UiDevice对象。

    我们怎么创建UiDevice对象呢?这时候的代码往往会提供另外一种创建对象的方式,Google在UiDevice类中提供了一个静态工厂方法getInstance()用于创建UiDevice对象,其中一个无参的静态方法getInstance()已被废弃,另一个重载的静态方法getInstance()则需要传入一个Instrumentation对象。

    学习UiDevice各种功能(API)的实现原理,可以帮助我们提高编写Ui自动化程序的能力,构建出更稳定、好维护、健壮性更好的大型Ui自动化项目。

    学习如何创建Device对象的代码,再温习一下UiDevice提供了哪些功能…………(第三篇文章已有介绍,这里是刻意温习的)

UiDevice提供了哪些功能呢? 

    UiDevice提供的常用功能可以分为下面几类(根据API):

1、操作设备

打开通知栏、按下back键、为设备截图

2、查找控件

获取表示View树中某一个具体的控件的对象(UiObject对象、UiObject2对象等等)

3、等待功能

插桩测试暂时停止运行,比如某个控件的可见性是由网络响应返回的某个字段来控制的,此时的插桩测试需要等待网络请求返回,才能再去获取指定的控件,UiDevice可以模拟这个等待功能

4、管理UiWatcher

包括注册、反注册、状态信息,使用UiWatcher对象可以大大提高测试代码的健壮性!

5、执行Android系统的命令

静态方法getInstance()分析

    public static UiDevice getInstance(Instrumentation instrumentation) {
        if (sInstance == null) {
            sInstance = new UiDevice(instrumentation);
        }
        return sInstance;
    }

getInstance()位于UiDevice类中,需要传入一个Instrumentation对象,返回值为创建的UiDevice对象

如何获取到一个Instrumentation对象?

答:使用InstrumentationRegistry类的静态方法getInstrumentation()获取,Instrmentation对象的知识点非常重要,这里暂时知道需要传入一个Instrmentation对象即可(它是Android系统App框架层中的知识点)

静态方法getInstance()方法的方法体主要有两个步骤

1、判断UiDevice对象是否已经创建,若没有创建则执行创建UiDevice对象

首先判断UiDevice类持有的sInstance(static修饰)是否为null,当sInstance为null,则说明UiDevice对象并没有创建,此时执行if代码块中的new语句创建一个UiDevice对象,在当前静态方法getInstance()传入的Instrumentation对象会继续再传入到UiDevice的一个参数的构造方法中,UiDevice对象创建完成后,会将自己的引用赋值给UiDevice类持有的sInsance

2、向调用者返回创建的UiDevice对象

最终该方法会将UiDevice类持有的sInstance指向的UiDevice对象的引用返回给调用者,这就是创建UiDevice对象的过程,可见UiDevice对象是以单例对象形式存在的,内存中只有一个UiDevice对象!

接下来我们一起研究UiDevice的1个参数的构造方法是如何实现的?

UiDevice单个参数的构造方法分析

    UiDevice(Instrumentation instrumentation) {
        mInstrumentation = instrumentation;
        mQueryController = new QueryController(instrumentation);
        mInteractionController = new InteractionController(instrumentation);

        // Enable multi-window support for API level 21 and up
        if (UiDevice.API_LEVEL_ACTUAL >= Build.VERSION_CODES.LOLLIPOP) {
            // Subscribe to window information
            AccessibilityServiceInfo info = getUiAutomation().getServiceInfo();
            info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
            getUiAutomation().setServiceInfo(info);
        }
    }

用于创建UiDevice对象的构造方法,必须指定一个Instrumentation对象

在方法的内部,执行了这些逻辑

1、UiDevice对象持有传入的Instrumentation对象 

首先将传入的Instrumentation对象赋值给UiDevice对象持有的实例变量mInstrumentation,即UiDevice对象持有一个Instrumentation对象

2、为UiDevice对象持有的QueryController对象进行初始化

创建QueryController对象,且必须指定传入的Instrumentation对象。UiDevice对象持有的实例变量mQueryController负责保存创建好的QueryController对象。UiDevice对象持有的QueryController对象有什么用呢?这个留个关子,继续往下看代码……

3、为UiDeivce对象持有的InteractionController对象进行初始化

接下来创建InteractionController对象,InteractionController对象也需要一个Instrumentation对象,这里同样将传入的Instrumentation传给InteractionController。UiDevice对象持有的mInteractionController指向创建的InteractionController对象。此时UiDevice对象持有的InteractionController对象有什么用呢?继续看看代码……

PS:停下来,思考一下代码……

疑问1、UiDevice对象持有的3个对象,Instrumentation对象、QueryController对象、还有InteractionController对象,这3个对象有什么用?它们有什么功能呢?

疑问2、为什么UiDevice对象、QueryController对象、InteractionController对象都持有同一个从UiDevice构造方法中传入的Instrumentation对象呢?这个Instrumentation对象就这么牛逼吗?

4、多窗口的逻辑(API 21及以上版本支持多窗口)

常量LOLLIPOP位于Build.VERSION_CODES类中,它的值是21,表示Android API等级是21,API 21对应的Android版本是5.0!当UiDevice的API版本大于等于21时(Android5.0),调用getUiAutomation()方法得到一个UiAutomation对象,再通过UiAutomation对象的getServiceInfo()方法得到一个AccessibilityServiceInfo对象,并由局部变量info负责存储。接着将局部变量info保存的AccessibilityServiceInfo对象的flags属性执行一个位或运算,运算结果再赋值给它的flags属性,接下来又一次使用getUiAutomation()方法获取UiAutomation对象,然后调用UiAutomation对象的setServiceInfo()方法将修改过的flags属性的AccessibilityServiceInfo对象再次由UiAutomation对象持有!

PS:停下来,再次思考代码……

疑问3、这个UiAutomation对象有什么作用呢?(备注:用于和AccessbilityManagerService建立连接)

重要字段介绍

1、静态变量(UiDevice类持有)

 API_LEVEL_ACTUAL:用于表示实际的Android API版本

KEY_PRESS_EVENT_TIMEOUT:输入事件的超时时间,默认值为1s

LOG_TAG:常量,用于调试时,使用的TAG_NAME

sInstance:UiDevice持有的,负责保存创建的UiDevice单例对象

2、实例变量(UiDevice对象持有)

mInstrumentation:UiDevice对象持有的Instrumentation对象,该对象提供的功能用于在App框架中使用

mInteractionController:UiDevice对象持有的InteractionController对象,该对象是干啥的?还不知道……囧……

mInWatcherContext:UiDevice对象持有的一个标志位,用于记录是否正在执行UiWatcher的业务逻辑

mQueryController:UiDevice对象持有的QueryController对象,该对象有什么功能呢?囧……

mWaitMixin:UiDevice对象持有的WaitMixin对象,该对象提供了各种根据条件让插桩线程暂停运行的等待功能

mWatchers:UiDevice对象持有的一个HashMap对象,此HashMap对象负责保存注册的每个UiWatcher对象

mWatchersTriggers:UiDevice对象持有的一个ArrayList对象,此ArrayList对象负责保存哪些已经被触发的UiWatcher对象的名字

总结

1、UiDevice对象在插桩测试项目中是作为单例对象存在的,通过UiDevice类的静态方法getInstance(Instramentation)即可获得UiDevice单例对象

2、UiDevice对象持有的Instrumentation对象、QueryController对象、InteractionController对象、以及并没有直接持有的,且使用的Uiautomation对象,它们各自都有什么用途?跟随我的脚步,你一定会知道所有……继续学习源码

3、Instrumentation对象非常重要,通过他可以获取到的一个UiAutomation对象,而通过UiAutomation对象才可以操作Window中的View树

4、Instrumentation对象,在以API M作为版本分隔中,获取方式还是不同的……

5、当我们创建UiDevice对象,UiDevice持有的一些对象也得到了创建,包括QueryConroller对象等等

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值