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

对控件的操作,主要在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方法中完成实际的操作。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012439416/article/details/70186032
个人分类: ---【自动化测试】
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

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

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭