UiAutomator源代码分析之UiAutomatorBridge框架

上一篇文章《UIAutomator源代码分析之启动和执行》我们描写叙述了uitautomator从命令行执行到载入測试用例执行測试的整个流程。过程中我们也描写叙述了UiAutomatorBridge这个类的重要性,说它相当于UiAutomation的代理(我们都知道UiAutomator是通过UiAutomation和AccessibilityService进行连接然后获取界面空间信息和注入事件的).那么今天開始我们就环绕这个类以及跟它有关系的类进行进一步的分析。


1. UiAutomatorBridge框架

这一章节我们会先看UiAutomatorBridge的总体框架,往后我会编写其它文章通过一些详细的样例把它们串起来。由于我的mackbook pro上没有安装类图软件,所下面图是手画的


往下我们就去初步描写叙述下UiAutomatorBridge跟每个相关的类的关系。


2. UiAutomatorBridge与UiAutomation的聚合关系

UiAutomatorBridge拥有一个UiAutomation的成员变量,它们是聚合的关系,注意不是组合,由于UiAutomation不一定仅仅能依赖UiAutomatorBridge而存在,我们上一章节的UiAutomatorTestRunner就拥有一个UiAutomation的成员变量。

一旦UiAutomator工具须要通过UiAutomatorBridge获取界面或者注入事件的时候。就会调用该成员变量.比方以下这个非常关键的去获取当前界面的Root Node的方法:

/*     */   public AccessibilityNodeInfo getRootInActiveWindow() {
/*  66 */     return this.mUiAutomation.getRootInActiveWindow();
/*     */   }


3. UiAutomatorBridge与QueryController的关联关系

QueryController做的全部事情就是去把UiSelector这个UI控件选择子翻译成真实的适合我们使用的android.view.accessibility.AccessibilityNodeInfo。

UiAutomatorBridge拥有一个成员变量mQueryController保存了QueryController的一个实例:

/*     */   private final QueryController mQueryController;
/*     */   
当UiObject须要获取一个UiSelector指定的控件信息时,会去调用UiAutomatorBridge的getQueryController方法来获得这个mQueryController对象来进行对应的操作。例如以下面的UiObject的方法findAccessibilityNodeInfo就是这样做的:

/*      */   protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout)
/*      */   {
/*  164 */     AccessibilityNodeInfo node = null;
/*  165 */     long startMills = SystemClock.uptimeMillis();
/*  166 */     long currentMills = 0L;
/*  167 */     while (currentMills <= timeout) {
/*  168 */       node = getQueryController().findAccessibilityNodeInfo(getSelector());
/*  169 */       if (node != null) {
/*      */         break;
/*      */       }
/*      */       
/*  173 */       UiDevice.getInstance().runWatchers();
/*      */       
/*  175 */       currentMills = SystemClock.uptimeMillis() - startMills;
/*  176 */       if (timeout > 0L) {
/*  177 */         SystemClock.sleep(1000L);
/*      */       }
/*      */     }
/*  180 */     return node;
/*      */   }

该getQueryController方法会去调用UiAutomatorBridge的getQueryController方法:

/*      */   QueryController getQueryController()
/*      */   {
/*  100 */     return UiDevice.getInstance().getAutomatorBridge().getQueryController();
/*      */   }
从上面的类图我们能够看到,除了UiAutomatorBridge会调用QueryController做事情外,QueryController又会反过来调用UiAutomatorBridge来做事情,由于如图所描写叙述的,仅仅有UiAutomatorBridge拥有UiAutomation的实例,所以QueryController会持有一个UiAutomatorBridge的实例:

/*     */   private final UiAutomatorBridge mUiAutomatorBridge;
然后在须要的时候再调用UiAutomatorBridge,如以下的获得Root Node的方法:

/*     */   protected AccessibilityNodeInfo getRootNode()
/*     */   {
/* 168 */     int maxRetry = 4;
/* 169 */     long waitInterval = 250L;
/* 170 */     AccessibilityNodeInfo rootNode = null;
/* 171 */     for (int x = 0; x < 4; x++) {
/* 172 */       rootNode = this.mUiAutomatorBridge.getRootInActiveWindow();
/* 173 */       if (rootNode != null) {
/* 174 */         return rootNode;
/*     */       }
/* 176 */       if (x < 3) {
/* 177 */         Log.e(LOG_TAG, "Got null root node from accessibility - Retrying...");
/* 178 */         SystemClock.sleep(250L);
/*     */       }
/*     */     }
/* 181 */     return rootNode;
/*     */   }

4. UiAutomatorBridge与InteractionController的关联关系

道理与以上的QueryController一样。仅仅是UiAutomatorBridge须要通过InteractionController做的事情不是去获得控件信息。而是去注入事件



5. UiAutomatorBridge与ShellUiAutomatorBridge的继承关系

UiAutomatorBridge是一个抽象类,里面的方法有下面几个:

  • getRootInActiveWindow :通过UiAutomation获取当前窗体控件xml信息的根节点(通过它能够循环获取全部控件)
  • injectInputEvent :通过UiAutomation注入事件
  • waitForIdle :   通过UiAutomation睡眠指定时间

  • executeCommandAndWaitForAccessibilityEvent:通过UiAutomation运行指定线程的操作然后等待预期的时间返回

  • takeScreenshot :通过UiAutomation进行截图

  • performGlobalAction :  通过UiAutomation去运行一些全局的动作。如打开近期打开过的app列表,回到home界面等

从中能够看到这些动过都是须要通过UiAutomation来运行的,但也有一些动作是不须要用UiAutomation运行的,所以我相信google是为了代码清晰和可维护性,提供了子类ShellUiAutomatorBridge来专门处理那些不须要用到UiAutomation的情况,比方下面的isScreenOn方法就不须要用到UiAutomation,而是直接用PowerManager服务来推断当前屏幕是否是打开的:

/*     */   public boolean isScreenOn()
/*     */   {
/* 111 */     IPowerManager pm = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
/*     */     
/* 113 */     boolean ret = false;
/*     */     try {
/* 115 */       ret = pm.isScreenOn();
/*     */     } catch (RemoteException e) {
/* 117 */       Log.e(LOG_TAG, "Error getting screen status", e);
/* 118 */       throw new RuntimeException(e);
/*     */     }
/* 120 */     return ret;
/*     */   }


 

作者

自主博客

微信

CSDN

天地会珠海分舵

http://techgogogo.com


服务号:TechGoGoGo

扫描码:

http://blog.csdn.net/zhubaitian





posted @ 2016-04-20 15:20  zfyouxi  阅读(1403)  评论(0编辑  收藏  举报