Uiautomator 源码分析之点击事件

uiautomator一个简单的脚本

public class UiautomatorTest extends UiAutomatorTestCase{

    public void testDemo() throws UiObjectNotFoundException {
        
    //   模拟 HOME 键点击事件
        getUiDevice().pressHome();

}

1、uiautomatorTest继承于UiAutomatorTestCase,通过父类获得Uidevice类实例,并调用父类方法pressHome();

2、查看pressHome方法源码

    /**
     * Simulates a short press on the HOME button.
     * @return true if successful, else return false
     * @since API Level 16
     */
    public boolean pressHome() {
        Tracer.trace();
        waitForIdle();
        return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent(
                KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
                KEY_PRESS_EVENT_TIMEOUT);
    }

getAutomatorBridge()方法获得InstrumentationUiAutomatorBridge实例,UiDevice类保存了

UiAutomatorBridge对象
    InstrumentationUiAutomatorBridge getAutomatorBridge() {
        if (mUiAutomationBridge == null) {
            throw new RuntimeException("UiDevice not initialized");
        }
        return mUiAutomationBridge;
    }
  • 通过UiAutomatorBridge对象获得InteractionController对象
  • 调用InteractionController对象的sendKeyAndWaitForEvent方法,里面参数关键是第一个keycode和第二个eventType

3、先来看下UiAutomatorBridge类何时初始化的还有InteractionController对象又是什么?

    /**
     * @hide
     */
    public void initialize(InstrumentationUiAutomatorBridge uiAutomatorBridge) {
        mUiAutomationBridge = uiAutomatorBridge;
    }

这个是隐藏的方法,初始化UiDevice类时直接传入UiAutomatorBridge对象,那么改函数何时执行尼?

查看源码发现这个函数是在UiDevice类里面调用的

    /**
     * Initializes this test case.
     *
     * @param params Instrumentation arguments.
     */
    void initialize(Bundle params) {
        mParams = params;

        // check if this is a monkey test mode
        String monkeyVal = mParams.getString("monkey");
        if (monkeyVal != null) {
            // only if the monkey key is specified, we alter the state of monkey
            // else we should leave things as they are.
            getInstrumentation().getUiAutomation().setRunAsMonkey(Boolean.valueOf(monkeyVal));
        }
       //这里传入new一个InstrumentationUiAutomatorBridge,参数一:上下文对象,参数二:UiAutomation实例
        UiDevice.getInstance().initialize(new InstrumentationUiAutomatorBridge(
                getInstrumentation().getContext(),
                getInstrumentation().getUiAutomation()));
    }

InstrumentationUiAutomatorBridge类继承于UiAutomatorBridge,看其构造函数

    public InstrumentationUiAutomatorBridge(Context context, UiAutomation uiAutomation) {
        super(uiAutomation);
        mContext = context;
    }

说明一下:context上下问对象是在启动设备的时候获取的,通过Instrumentation类,在android中Instrumentation类是属于观察者的角色,在Activity 的onCreate方法执行是进行实例化。如果不明白可以看看Instrumentation相关文章,这里不扯远了

说下InteractionController这个类,看下类的隶属关系InstrumentationUiAutomatorBridge extends UiAutomatorBridge,getInteractionController方法是在UiAutomatorBridge里面的。UiAutomatorBridge(UiAutomation uiAutomation) { //new UiAutomatorBridge对象的时候直接初始化参数

mUiAutomation = uiAutomation; mInteractionController = new InteractionController(this); mQueryController = new QueryController(this); // } InteractionController getInteractionController() { return mInteractionController; }

这个uiAutomator参数又是上面提到的initialize()这个方法传递进去的

InteractionController这个类是主要提供操作控件的方法,比如touchUp(int x, int y)、scrollSwipe、sendKeyAndWaitForEvent等等方法。

4、回到上面sendKeyAndWaitForEvent()方法,看一下源码

    /**
     * Send keys and blocks until the first specified accessibility event.
     *
     * Most key presses will cause some UI change to occur. If the device is busy, this will
     * block until the device begins to process the key press at which point the call returns
     * and normal wait for idle processing may begin. If no events are detected for the
     * timeout period specified, the call will return anyway with false.
     *
     * @param keyCode      //key类型代码
     * @param metaState     //代表设备状态,忙或是空闲
     * @param eventType    //事件类型
     * @param timeout        //超时时间
     * @return true if events is received, otherwise false.
     */
    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); //转换成对应的event类型
                if (injectEventSync(downEvent)) {  //down 按下
                    KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP,
                            keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
                            InputDevice.SOURCE_KEYBOARD);
                    injectEventSync(upEvent);     //up抬起
                }
            }
        };

        return runAndWaitForEvents(command, new WaitForAnyEventPredicate(eventType), timeout)
                != null;    //等待操作完成,真正执行run操作在这个函数,看 runAndWaitForEvents }
    private AccessibilityEvent runAndWaitForEvents(Runnable command,
            AccessibilityEventFilter filter, long timeout) {

        try {
            return mUiAutomatorBridge.executeCommandAndWaitForAccessibilityEvent(command, filter,
                    timeout);    //执行相关命令 注意command这个是线程对象
        } 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类

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

继续跳到Uiautomator这个类,最终是通过同步操作的方法进行,其他都是一些等待事件超时的函数检测机制,具体怎么实现也是不太看得懂

     * @param command The command to execute.
     * @param filter Filter that recognizes the expected event.
     * @param timeoutMillis The wait timeout in milliseconds.
     *
     * @throws TimeoutException If the expected event is not received within the timeout.
     */
    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();
            }
        }
    }

5、返回来看看刚才上面的inject操作

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

同样是使用UiAutomatorBridge类的injectInputEvent方法进行,最终还是和executeAndWaitForEvent方法同一个类

    public boolean injectInputEvent(InputEvent event, boolean sync) {
        return mUiAutomation.injectInputEvent(event, sync);
    }
    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;
    }

这里用到了IUiAutomationConnection类,这个类是什么鬼尼,不知道没找到源码但是可以找到该类实例是如何过来的,是在Instrumentation类初始化函数里面

    /*package*/ final void init(ActivityThread thread,
            Context instrContext, Context appContext, ComponentName component, 
            IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection) {
        mThread = thread;
        mMessageQueue = mThread.getLooper().myQueue();
        mInstrContext = instrContext;
        mAppContext = appContext;
        mComponent = component;
        mWatcher = watcher;
        mUiAutomationConnection = uiAutomationConnection;     //在这里,应该是activity启动时给初始化的同Instrumentation一样    }

Instrumentation类是前文提到的UiAutomatorTestCase类初始化来的。

至此分析到这里,这里的关系错综复杂,自己都搞蒙了只能慢慢看慢慢消化了,最后简单说明下各个类之间的关系,是别人整理过的:

1. UiAutomatorBridge与UiAutomation的聚合关系
UiAutomatorBridge拥有一个UiAutomation的成员变量,它们是聚合的关系,注意不是组合,因为 UiAutomation不一定只能依赖UiAutomatorBridge而存在 一旦UiAutomatorBridge工具需要通过UiAutomator获取界面或者注入事件的时候,就会调用该成员变量.比如下面这个很关键的去获取当前界面的Root Node的方法:
[java]   view plain   copy
  1. /*     */   public AccessibilityNodeInfo getRootInActiveWindow() {  
  2. /*  66 */     return this.mUiAutomation.getRootInActiveWindow();  
  3. /*     */   }  
2. UiAutomatorBridge与InteractionController的关联关系
道理与以上的QueryController一样(该类获取相关控件信息),只是 UiAutomatorBridge需要通过InteractionController做的事情不是去获得控件信息,而是去注入事件 。当然这个类只是提供了InteractionController的成员变量而言,起到中转的作用



你可能感兴趣的:(Uiautomator)