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

原创 2017年04月15日 13:04:03

在测试中,可以对应设备(UiDevice)进行很多操作, 可以检测设备的各种属性,例如当前的屏幕方向以及屏幕尺寸;

同时还可以通过UiDevice实例来执行设备级别的操作,例如 把设备设置为横屏或者竖屏、按下Home按键等

这些操作的具体流程以及逻辑大同小异,以pressHome方法为例来论述其原理。

UiDevice 的pressHome方法如下,

public boolean pressHome() {
Tracer.trace();
waitForIdle();
return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent(
   KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
                KEY_PRESS_EVENT_TIMEOUT);
    }

getAutomatorBridge方法返回UiAutomatorBridge对象,如下,

UiAutomatorBridge getAutomatorBridge() {
        if (mUiAutomationBridge == null) {
            throw new RuntimeException("UiDevice not initialized");
        }
        return mUiAutomationBridge;
    }

ShellUiAutomatorBridge对象在UiAutomatorTestRunner的start方法中创建并赋值给UiDevice的变量mUiAutomationBridge

mUiDevice = UiDevice.getInstance();
mUiDevice.initialize(new ShellUiAutomatorBridge(automationWrapper.getUiAutomation()));

很明显, UiDevice是一个单例,其initialize方法如下,

public void initialize(UiAutomatorBridge uiAutomatorBridge) {
        mUiAutomationBridge = uiAutomatorBridge;
    }

ShellUiAutomatorBridge继承于UiAutomatorBridge。UiAutomatorBridge中的getInteractionController方法如下,

InteractionController getInteractionController() {
        return mInteractionController;
    }

获取的是InteractionController对象,在UiAutomatorBridge的构造方法中实例化,

UiAutomatorBridge(UiAutomation uiAutomation) {
        mUiAutomation = uiAutomation;
        mInteractionController = new InteractionController(this);
        mQueryController = new QueryController(this);
    }

InteractionController的sendKeyAndWaitForEventG方法如下,

public boolean sendKeyAndWaitForEvent(final int keyCode, final int metaState,
            final int eventType, long timeout) {
        Runnable command = new Runnable() {
            @Override
            public void run() {
                final long eventTime = SystemClock.uptimeMillis();
                KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN,
                        keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
                        InputDevice.SOURCE_KEYBOARD);
                if (injectEventSync(downEvent)) {
                    KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP,
                            keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
                            InputDevice.SOURCE_KEYBOARD);
                    injectEventSync(upEvent);
                }
            }
        };

        return runAndWaitForEvents(command, new WaitForAnyEventPredicate(eventType), timeout)
                != null;
    }

先抛开其他方法的调用,肯定会回调run方法,然后首先构造一个KeyEvent down事件,然后输入到系统;成功后,构造一个up KeyEvent事件,输入到系统中。

所以分为2个步骤,看看是如何回调的。

1, runAndWaitForEvents

2, injectEventSync

1.1 runAndWaitForEvents

runAndWaitForEvents方法如下,

private AccessibilityEvent runAndWaitForEvents(Runnable command,
            AccessibilityEventFilter filter, long timeout) {

        try {
            return mUiAutomatorBridge.executeCommandAndWaitForAccessibilityEvent(command, filter,
                    timeout);
        } catch (TimeoutException e) {
            Log.w(LOG_TAG, "runAndwaitForEvent timedout waiting for events");
            return null;
        } catch (Exception e) {
            Log.e(LOG_TAG, "exception from executeCommandAndWaitForAccessibilityEvent", e);
            return null;
        }
    }

UiAutomatorBridge 的executeCommandAndWaitForAccessibilityEvent的方法如下,

public AccessibilityEvent executeCommandAndWaitForAccessibilityEvent(Runnable command,
            AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException {
        return mUiAutomation.executeAndWaitForEvent(command,
                filter, timeoutMillis);
    }

mUiAutomation对象是在UiAutomatorTestRunner的start方法中通过调用UiAutomationShellWrapper的connect而创建的。

executeAndWaitForEvent方法如下,

public AccessibilityEvent executeAndWaitForEvent(Runnable command,
            AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException {
        // Acquire the lock and prepare for receiving events.
        synchronized (mLock) {
            throwIfNotConnectedLocked();
            mEventQueue.clear();
            // Prepare to wait for an event.
            mWaitingForEventDelivery = true;
        }

        // Note: We have to release the lock since calling out with this lock held
        // can bite. We will correctly filter out events from other interactions,
        // so starting to collect events before running the action is just fine.

        // We will ignore events from previous interactions.
        final long executionStartTimeMillis = SystemClock.uptimeMillis();
        // Execute the command *without* the lock being held.
        command.run();

        // Acquire the lock and wait for the event.
        synchronized (mLock) {
            try {
                // Wait for the event.
                final long startTimeMillis = SystemClock.uptimeMillis();
                while (true) {
                    // Drain the event queue
                    while (!mEventQueue.isEmpty()) {
                        AccessibilityEvent event = mEventQueue.remove(0);
                        // Ignore events from previous interactions.
                        if (event.getEventTime() < executionStartTimeMillis) {
                            continue;
                        }
                        if (filter.accept(event)) {
                            return event;
                        }
                        event.recycle();
                    }
                    // Check if timed out and if not wait.
                    final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
                    final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
                    if (remainingTimeMillis <= 0) {
                        throw new TimeoutException("Expected event not received within: "
                                + timeoutMillis + " ms.");
                    }
                    try {
                        mLock.wait(remainingTimeMillis);
                    } catch (InterruptedException ie) {
                        /* ignore */
                    }
                }
            } finally {
                mWaitingForEventDelivery = false;
                mEventQueue.clear();
                mLock.notifyAll();
            }
        }
    }

在该方法实现回调,并且做一些其它的处理。

1.2, injectEventSync

InteractionController的injectEventSync方法如下,

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

UiAutomatorBridge的injectInputEvent方法如下,

public boolean injectInputEvent(InputEvent event, boolean sync) {
        return mUiAutomation.injectInputEvent(event, sync);
    }

UiAutomation的injectInputEvent方法如下,

public boolean injectInputEvent(InputEvent event, boolean sync) {
        synchronized (mLock) {
            throwIfNotConnectedLocked();
        }
        try {
            if (DEBUG) {
                Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync);
            }
            // Calling out without a lock held.
            return mUiAutomationConnection.injectInputEvent(event, sync);
        } catch (RemoteException re) {
            Log.e(LOG_TAG, "Error while injecting input event!", re);
        }
        return false;
    }

UiAutomationConnection的injectInputEvent方法如下,

@Override
    public boolean injectInputEvent(InputEvent event, boolean sync) {
        synchronized (mLock) {
            throwIfCalledByNotTrustedUidLocked();
            throwIfShutdownLocked();
            throwIfNotConnectedLocked();
        }
        final int mode = (sync) ? InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
                : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC;
        final long identity = Binder.clearCallingIdentity();
        try {
            return InputManager.getInstance().injectInputEvent(event, mode);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

最后通过InputManager来输入模拟按键事件。

UiAutomationShellWrapper的connect方法如下,

public void connect() {
        if (mHandlerThread.isAlive()) {
            throw new IllegalStateException("Already connected!");
        }
        mHandlerThread.start();
        mUiAutomation = new UiAutomation(mHandlerThread.getLooper(),
                new UiAutomationConnection());
        mUiAutomation.connect();
    }

UiAutomationConnection也是在该方法中创建的,然后利用该对象创建UiAutomation对象。

所以实际上,调用的流程图如下,

UiDevice 》UiAutomatorBridge 》InteractionController 》UiAutomatorBridge 》UiAutomation

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

UiAutomationConnection是测试和系统之间的桥梁。

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

Uiautomator——API详解

原文链接:http://www.cnblogs.com/by-dream/p/4921701.html#3328376 以一个简单的例子开始吧。我们完成一个 " 打开QQ,进入Q...
  • QingLang0213
  • QingLang0213
  • 2015年12月18日 17:08
  • 10557

uiautomator API文档

uiautomator API分享:      百度网盘:http://pan.baidu.com/s/1sjvE4cl      密码: mcig
  • vivian_ljx
  • vivian_ljx
  • 2015年08月28日 13:01
  • 1820

关于UIautomator API及浅析

从android sdk api 16开始,Android SDK开始支持两个做功能UI测试的新工具。 uiautomatorviewer,一个用以扫描以及分析Android应用程序的UI部件的工具...
  • Mind_Reader
  • Mind_Reader
  • 2015年10月08日 17:48
  • 2553

UiAutomator API文档

  • 2014年11月16日 16:28
  • 146KB
  • 下载

Uiautomator 2.0之UiDevice新增API学习小记

1. InstrumentationRegistry类 1.1. 类说明: 一个暴露的注册实例,持有instrumentation运行的进程和参数,还提供了一种简便的方法调用instrumentati...
  • swordgirl2011
  • swordgirl2011
  • 2016年03月21日 00:28
  • 8562

Android uiautomator自动化API简介

  • 2014年06月05日 15:12
  • 34KB
  • 下载

UiAutomator笔记之UiObject API(四)

一、UiObject 功能 UiObject用来代表一个组件对象,它提供一系列方法和属性来模拟在手机上的实际操作。如:文本的输入和清除、点击、长按、拖动、滑动,也可获取组件的属性、判断组件对象是否存在...
  • yiwaChen
  • yiwaChen
  • 2016年09月19日 23:14
  • 2275

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

对控件的操作,主要在UiObject中。例如各种点击事件。 以长按事件来论述详细的流程。 UiObject的longClick方法如下, public boolean longClick() t...
  • u012439416
  • u012439416
  • 2017年04月15日 18:28
  • 613

UiAutomator(2)--UiDevice API

UiAutomator(2)--UiDevice API 一、UiDevice介绍        1.UiDevice代表设备状态,为单例模式。UiDevice的功能包括:           ...
  • u013512708
  • u013512708
  • 2016年11月03日 17:43
  • 1421

UiAutomator(5)--UiScrollable API

UiAutomator(5)--UiScrollable API 一、UiScrollable介绍    1.UiScrollable是UiCollection的子类    2.UiScroll...
  • u013512708
  • u013512708
  • 2016年11月03日 17:48
  • 242
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android自动化测试Uiautomator API分析之一
举报原因:
原因补充:

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