從上一章對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層的邏輯了。