android自动化测试Uiautomator API分析之二

原创 2017年04月15日 18:28:05

对控件的操作,主要在UiObject中。例如各种点击事件。

以长按事件来论述详细的流程。

UiObject的longClick方法如下,

public boolean longClick() throws UiObjectNotFoundException  {
        Tracer.trace();
        AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
        if(node == null) {
            throw new UiObjectNotFoundException(getSelector().toString());
        }
        Rect rect = getVisibleBounds(node);
        return getInteractionController().longTapNoSync(rect.centerX(), rect.centerY());
    }

首先调用findAccessibilityNodeInfo方法在当前屏幕重新查找该控件,

然后调用InteractionController 的longTapNoSync方法写入事件。

1.1 findAccessibilityNodeInfo

protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
        AccessibilityNodeInfo node = null;
        long startMills = SystemClock.uptimeMillis();
        long currentMills = 0;
        while (currentMills <= timeout) {
            node = getQueryController().findAccessibilityNodeInfo(getSelector());
            if (node != null) {
                break;
            } else {
                // does nothing if we're reentering another runWatchers()
                UiDevice.getInstance().runWatchers();
            }
            currentMills = SystemClock.uptimeMillis() - startMills;
            if(timeout > 0) {
                SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
            }
        }
        return node;
    }

getQueryController方法返回的是QueryController对象,然后调用findAccessibilityNodeInfo,

public AccessibilityNodeInfo findAccessibilityNodeInfo(UiSelector selector) {
        return findAccessibilityNodeInfo(selector, false);
    }

    protected AccessibilityNodeInfo findAccessibilityNodeInfo(UiSelector selector,
            boolean isCounting) {
        mUiAutomatorBridge.waitForIdle();
        initializeNewSearch();

        if (DEBUG)
            Log.d(LOG_TAG, "Searching: " + selector);

        synchronized (mLock) {
            AccessibilityNodeInfo rootNode = getRootNode();
            if (rootNode == null) {
                Log.e(LOG_TAG, "Cannot proceed when root node is null. Aborted search");
                return null;
            }

            // Copy so that we don't modify the original's sub selectors
            UiSelector uiSelector = new UiSelector(selector);
            return translateCompoundSelector(uiSelector, rootNode, isCounting);
        }
    }

首先调用getRootNode来获得根节点,

然后调用translateCompoundSelector来根据用户指定的UiSelector格式从上面获得根节点开始遍历窗口控件树,以获得目标控件。

1.1.1 getRootNode

getRootNode方法如下,

protected AccessibilityNodeInfo getRootNode() {
        final int maxRetry = 4;
        final long waitInterval = 250;
        AccessibilityNodeInfo rootNode = null;
        for(int x = 0; x < maxRetry; x++) {
            rootNode = mUiAutomatorBridge.getRootInActiveWindow();
            if (rootNode != null) {
                return rootNode;
            }
            if(x < maxRetry - 1) {
                Log.e(LOG_TAG, "Got null root node from accessibility - Retrying...");
                SystemClock.sleep(waitInterval);
            }
        }
        return rootNode;
}

调用UiAutomatorBridge 的getRootInActiveWindow方法,

public AccessibilityNodeInfo getRootInActiveWindow() {
        return mUiAutomation.getRootInActiveWindow();
    }

直接调用UiAutomation的getRootInActiveWindow方法,

public AccessibilityNodeInfo getRootInActiveWindow() {
        final int connectionId;
        synchronized (mLock) {
            throwIfNotConnectedLocked();
            connectionId = mConnectionId;
        }
        // Calling out without a lock held.
        return AccessibilityInteractionClient.getInstance()
                .getRootInActiveWindow(connectionId);
    }

很明显调用AccessibilityInteractionClient的getRootInActiveWindow方法,

AccessibilityInteractionClient是framework中的类,在此就不论述了。

1.1.2 translateCompoundSelector

translateCompoundSelector方法如下,

private AccessibilityNodeInfo translateCompoundSelector(UiSelector selector,
            AccessibilityNodeInfo fromNode, boolean isCounting) {

        // Start translating compound selectors by translating the regular_selector first
        // The regular_selector is then used as a container for any optional pattern_selectors
        // that may or may not be specified.
        if(selector.hasContainerSelector())
            // nested pattern selectors
            if(selector.getContainerSelector().hasContainerSelector()) {
                fromNode = translateCompoundSelector(
                        selector.getContainerSelector(), fromNode, false);
                initializeNewSearch();
            } else
                fromNode = translateReqularSelector(selector.getContainerSelector(), fromNode);
        else
            fromNode = translateReqularSelector(selector, fromNode);

        if(fromNode == null) {
            if (DEBUG)
                Log.d(LOG_TAG, "Container selector not found: " + selector.dumpToString(false));
            return null;
        }

        if(selector.hasPatternSelector()) {
            fromNode = translatePatternSelector(selector.getPatternSelector(),
                    fromNode, isCounting);

            if (isCounting) {
                Log.i(LOG_TAG, String.format(
                        "Counted %d instances of: %s", mPatternCounter, selector));
                return null;
            } else {
                if(fromNode == null) {
                    if (DEBUG)
                        Log.d(LOG_TAG, "Pattern selector not found: " +
                                selector.dumpToString(false));
                    return null;
                }
            }
        }

        // translate any additions to the selector that may have been added by tests
        // with getChild(By selector) after a container and pattern selectors
        if(selector.hasContainerSelector() || selector.hasPatternSelector()) {
            if(selector.hasChildSelector() || selector.hasParentSelector())
                fromNode = translateReqularSelector(selector, fromNode);
        }

        if(fromNode == null) {
            if (DEBUG)
                Log.d(LOG_TAG, "Object Not Found for selector " + selector);
            return null;
        }
        Log.i(LOG_TAG, String.format("Matched selector: %s <<==>> [%s]", selector, fromNode));
        return fromNode;
    }

循环嵌套调用,获取目标控件。

1.2 longTapNoSync

InteractionController的longTapNoSync方法如下,

public boolean longTapNoSync(int x, int y) {
        if (DEBUG) {
            Log.d(LOG_TAG, "longTapNoSync (" + x + ", " + y + ")");
        }

        if (touchDown(x, y)) {
            SystemClock.sleep(mUiAutomatorBridge.getSystemLongPressTime());
            if(touchUp(x, y)) {
                return true;
            }
        }
        return false;
    }

两个方法touchDown和touchUp方法。touchDown方法如下,

private boolean touchDown(int x, int y) {
        if (DEBUG) {
            Log.d(LOG_TAG, "touchDown (" + x + ", " + y + ")");
        }
        mDownTime = SystemClock.uptimeMillis();
        MotionEvent event = MotionEvent.obtain(
                mDownTime, mDownTime, MotionEvent.ACTION_DOWN, x, y, 1);
        event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
        return injectEventSync(event);
    }

首先构造一个MotionEvent对象,然后调用injectEventSync方法向系统注入该事件。

private boolean injectEventSync(InputEvent event) {
        return mUiAutomatorBridge.injectInputEvent(event, true);
    }

当然依次调用UiAutomatorBridge,UiAutomation

最后在UiAutomationConnection的injectInputEvent方法中完成实际的操作。

版权声明:本文为博主原创文章,未经博主允许不得转载。

UiAutomator源码分析之UiAutomatorBridge框架

上一篇文章《UIAutomator源码分析之启动和运行》我们描述了uitautomator从命令行运行到加载测试用例运行测试的整个流程,过程中我们也描述了UiAutomatorBridge这个类的重要...
  • zhubaitian
  • zhubaitian
  • 2014年10月28日 13:44
  • 7812

Android PopupWindow 7.0之后出现的问题

缘由 之前老项目一直有个问题,就是弹窗会顶在屏幕最上面,一直以为是特殊机型适配的问题,一拖再拖没有解决,后来测试实在忍不了,上网上查一查,发现这个bug出现好久了,是安卓7.0之后源码发生改变导...
  • panghaha12138
  • panghaha12138
  • 2018年01月11日 17:39
  • 61

AccessibilityService(无障碍服务)小结

https://developer.android.google.cn/guide/topics/ui/accessibility/services.html 无障碍服务,可以监听界面的操作,比如:...
  • a343649190
  • a343649190
  • 2017年02月10日 17:05
  • 865

Accessibilityservice学习

accessibilityservice是android上用来做辅助功能的一个API,主要用来相应用户发送AccessibilityEvent的事件,具体可以google或者baidu一下,不过只支持...
  • mockingbirds
  • mockingbirds
  • 2016年02月07日 20:12
  • 3101

UiAutomator源码分析之获取控件信息

根据上一篇文章《UiAutomator源码分析之注入事件》开始时提到的计划,这一篇文章我们要分析的是第二点: 如何获取控件信息 我们在测试脚本中初始化一个UiObject的时候通常是像以下这个样子:...
  • zhubaitian
  • zhubaitian
  • 2014年10月29日 09:25
  • 8307

UiAutomator注入和查找事件源码浅析

今天浅析的一个自动化框架是UiAutomator这个框架,那么从网上下了源码,就来分析一下数据流的走向吧,主要内容有两个,第一个是如果查找控件,第二个是事件是如何注入的。本人水平有限,所以只能是个浅析...
  • Cloud_Huan
  • Cloud_Huan
  • 2016年02月25日 18:29
  • 1617

基于uiautomator的android脚本录制记录

1
  • u010386438
  • u010386438
  • 2017年03月16日 17:18
  • 1257

UiAutomator笔记之UiSelector API(三)

一、UiSelector相关知识 1、UiSelector功能 UiSelector可通过控件的各种属性与节点关系定位组件。 2、Android常用组件 TextView 文本框 Edi...
  • yiwaChen
  • yiwaChen
  • 2016年09月17日 23:54
  • 1997

Android开发中AccessibilityService的使用

AccessibilityService在官网上的介绍如下: An accessibility service runs in the background and receives callbac...
  • cxq234843654
  • cxq234843654
  • 2016年02月22日 18:09
  • 3707

appium的一个内存泄露bug

基于appium源码 1.6.3 运行,发现运行一段时间后,会出现内存溢出. 用node-heapdump生成heap快照  发现多了很多字符串对象:内容如下 02-22 14:50:57.128 2...
  • zdhsoft
  • zdhsoft
  • 2017年02月23日 20:36
  • 862
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android自动化测试Uiautomator API分析之二
举报原因:
原因补充:

(最多只允许输入30个字)