• UiAutomator源码学习(2)-- UiAutomationBridge


    从上一章对UiDevice的学习,可以看出几乎所有的操作都离不开 UiAutomationBridge。重新看一下UIDevice的构造方法:

      private UiDevice(Instrumentation instrumentation) {
            mInstrumentation = instrumentation;
            UiAutomation uiAutomation = instrumentation.getUiAutomation();
            mUiAutomationBridge = new InstrumentationUiAutomatorBridge(
                    instrumentation.getContext(), uiAutomation);
            // Enable multi-window support for API level 21 and up
            if (UiDevice.API_LEVEL_ACTUAL >= Build.VERSION_CODES.LOLLIPOP) {
                // Subscribe to window information
                AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
                info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
                uiAutomation.setServiceInfo(info);
            }
        }

     UiAutomationBridge 是一个抽象类。我们先看UiDevice的构造函数中,UiAutomatorBridge的实现类InstrumentationUiAutomatorBridge。这个类比较简单复写了getRotation和isScreenOn方法。接下来我们看一下这个抽象类的构造方法:

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

    在这里初始化了 InteractionController和 QueryController这两个类的对象。在学习UiDevice的时候应该还记得,几乎所有的操作都是通过这两个类来完成的。这里是UiDevice里的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);
        }

    通过这个方法可以看到,这个InteractionController可以向系统注入事件。那接下来就来看看这个InteractionController到底是怎么向系统注入事件的。还是从构造方法看起:

     public InteractionController(UiAutomatorBridge bridge) {
            mUiAutomatorBridge = bridge;
        }

    这个InteractionController持有UiAutomatorBridge的引用。并且在这个类中定义了很多模拟用户的操作方法如,sendKeyAndWaitForEvent, touchDown,touchUp,swipe等,例如uiDevcie里用到的sendKeyAndWaitForEvent。

     1     /**
     2      * Send keys and blocks until the first specified accessibility event.
     3      *
     4      * Most key presses will cause some UI change to occur. If the device is busy, this will
     5      * block until the device begins to process the key press at which point the call returns
     6      * and normal wait for idle processing may begin. If no events are detected for the
     7      * timeout period specified, the call will return anyway with false.
     8      *
     9      * @param keyCode
    10      * @param metaState
    11      * @param eventType
    12      * @param timeout
    13      * @return true if events is received, otherwise false.
    14      */
    15     public boolean sendKeyAndWaitForEvent(final int keyCode, final int metaState,
    16             final int eventType, long timeout) {
    17         Runnable command = new Runnable() {
    18             @Override
    19             public void run() {
    20                 final long eventTime = SystemClock.uptimeMillis();
    21                 KeyEvent downEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN,
    22                         keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
    23                         InputDevice.SOURCE_KEYBOARD);
    24                 if (injectEventSync(downEvent)) {
    25                     KeyEvent upEvent = new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP,
    26                             keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0,
    27                             InputDevice.SOURCE_KEYBOARD);
    28                     injectEventSync(upEvent);
    29                 }
    30             }
    31         };
    32         return runAndWaitForEvents(command, new WaitForAnyEventPredicate(eventType), timeout)
    33                 != null;
    34     }

     Line17,定义一个Runnable对象,Runnable只是一个接口,它里面只有一个run()方法,没有start()方法,所以该对象无法启动线程,必须依托其他类来启动这个线程。

    在这个run方法中,定义了一个KeyEvent事件,KeyEnvet对象是android.view.*包下的类,用于报告键和按钮事件。每次按键是通过一系列按键事件来描述的。按键操作以ACTION_DOWN按键事件开始。如果密钥被保持足够长的时间以至于可以重复,则在初始按下后会出现其他具有ACTION_DOWN和getRepeatCount()非零值的密钥事件。最后一个按键事件是用于按键启动的ACTION_UP。如果取消按键,则按键事件将设置FLAG_CANCELED标志。

    这个run方法里还有一个if判断条件injectEventSync,通过这个方法名就可以看出这是用来判断同步注入事件是否成功,在injectEventSync方法中,它调用了mUiAutomatorBridge.injectInputEvent(event, true);而mUiAutomatorBridge这个类的injectInputEvent方法里,是调用的mUiAutomation.injectInputEvent(event, sync);而mUiAutomation是Android SDK中 android.app.UiAutomation这个类的对象,我们回过头来看各个函数的构造函数发现,这个UiAutomation来自于UiDevice:

    UiAutomation uiAutomation = instrumentation.getUiAutomation();

    来看一下这个类中定义的injectInputEvent事件:

    /**
         * A method for injecting an arbitrary input event.
         * <p>
         * <strong>Note:</strong> It is caller's responsibility to recycle the event.
         * </p>
         * @param event The event to inject.
         * @param sync Whether to inject the event synchronously.
         * @return Whether event injection succeeded.
         */
        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;
        }

    看来这里也不是真正做事件注入的地方,mUiAutomationConnection是一个接口对象,这个对象是在UiAutomaton构造函数里初始化的。看他的实现类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 mWindowManager.injectInputAfterTransactionsApplied(event, mode);
            } catch (RemoteException e) {
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
            return false;
        }
    private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
                ServiceManager.getService(Service.WINDOW_SERVICE));
    package android.os;
    public final class ServiceManager {
        public static IBinder getService(String name) {
            try {
                IBinder service = sCache.get(name);
                if (service != null) {
                    return service;
                } else {
                    return getIServiceManager().getService(name);
                }
            } catch (RemoteException e) {
                Log.e(TAG, "error in getService", e);
            }
            return null;
        }
    }

    从这里可以看出mWindowManager是一个IBinder对象,通过这个对象调用openSession打开一个Session,实现IPC通信。看一下WindowManagerService里的

    injectInputAfterTransactionsApplied方法:
     1  @Override
     2     public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) {
     3         boolean isDown;
     4         boolean isUp;
     5 
     6         if (ev instanceof KeyEvent) {
     7             KeyEvent keyEvent = (KeyEvent) ev;
     8             isDown = keyEvent.getAction() == KeyEvent.ACTION_DOWN;
     9             isUp = keyEvent.getAction() == KeyEvent.ACTION_UP;
    10         } else {
    11             MotionEvent motionEvent = (MotionEvent) ev;
    12             isDown = motionEvent.getAction() == MotionEvent.ACTION_DOWN;
    13             isUp = motionEvent.getAction() == MotionEvent.ACTION_UP;
    14         }
    15         final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE;
    16 
    17         // For ACTION_DOWN, syncInputTransactions before injecting input.
    18         // For all mouse events, also sync before injecting.
    19         // For ACTION_UP, sync after injecting.
    20         if (isDown || isMouseEvent) {
    21             syncInputTransactions();
    22         }
    23         final boolean result =
    24                 LocalServices.getService(InputManagerInternal.class).injectInputEvent(ev, mode);
    25         if (isUp) {
    26             syncInputTransactions();
    27         }
    28         return result;
    29     }
    syncInputTransactions()这个方法是同步系统注入事件的事物,对于action up事件是在注入之后同步,其他的事件是在事件注入之前同步。 我们主要看一下事件注入.
    LocalServices 的getService方法,返回一个实现了InputManagerInternal类型的Service, InputManagerInternal是一个抽象类,而injectInputEvent也是一个抽象方法。
    那接下来我们就看一下这个InputManger类型的service。这是一个系统的服务 SystemService。
     1 /**
     2      * Injects an input event into the event system on behalf of an application.
     3      * The synchronization mode determines whether the method blocks while waiting for
     4      * input injection to proceed.
     5      * <p>
     6      * Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into
     7      * windows that are owned by other applications.
     8      * </p><p>
     9      * Make sure you correctly set the event time and input source of the event
    10      * before calling this method.
    11      * </p>
    12      *
    13      * @param event The event to inject.
    14      * @param mode The synchronization mode.  One of:
    15      * {@link #INJECT_INPUT_EVENT_MODE_ASYNC},
    16      * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT}, or
    17      * {@link #INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH}.
    18      * @return True if input event injection succeeded.
    19      *
    20      * @hide
    21      */
    22     @UnsupportedAppUsage
    23     public boolean injectInputEvent(InputEvent event, int mode) {
    24         if (event == null) {
    25             throw new IllegalArgumentException("event must not be null");
    26         }
    27         if (mode != INJECT_INPUT_EVENT_MODE_ASYNC
    28                 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
    29                 && mode != INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
    30             throw new IllegalArgumentException("mode is invalid");
    31         }
    32         try {
    33             return mIm.injectInputEvent(event, mode);
    34         } catch (RemoteException ex) {
    35             throw ex.rethrowFromSystemServer();
    36         }
    37     }

    Line33,调用的是IInputManager.aidl里的injectInputEvent,通过进程之间的通信,实现了系统的事件注入。到此事件注入的流程分析完毕,先到此为止。再想深入研究就是Native层的逻辑了。

  • 相关阅读:
    xhtml中隐藏滚动条
    一个题目包括70种网页制作常用技巧 (转)
    Oracle 数据类型映射 (ADO.NET)
    oracle数据库 数据类型
    VisualSVN Server的配置和使用方法
    oracle 权限管理
    项目管理实践【三】每日构建【Daily Build Using CruiseControl.NET and MSBuild】
    showModalDialog和showModelessDialog使用心得
    Bug跟踪管理【Bug Trace and Management】
    开发WAP站点之使用PC电脑浏览器访问WAP手机站点 (转)
  • 原文地址:https://www.cnblogs.com/yuan1225/p/13254701.html
Copyright © 2020-2023  润新知