2015/04/27

Android UI AutomatorViewer (開發筆記-2)

今天來看 AutomatorViewer 的源碼

首先進入點是 UiAutomatorViewer

重要的後端邏輯處裡類別有 DebugBridge、UiAutomatorHelper、UiAutomatorModel ,以及 Action 系列 (在 com.android.uiautomator.actions package 下),而前端操作的邏輯部分為 UiAutomatorView,其中註冊多個 Listener 與 UiAutomatorModel 做溝通

UiAutomatorViewer 在最初開始連結 adb 後,建立基本的操作介面

DebugBridge.init();

ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
toolBarManager.add(new OpenFilesAction(this));
toolBarManager.add(new ScreenshotAction(this,false));
toolBarManager.add(new ScreenshotAction(this,true));
toolBarManager.add(new SaveScreenShotAction(this));
ToolBar tb = toolBarManager.createControl(c);
tb.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mUiAutomatorView = new UiAutomatorView(c, SWT.BORDER);
mUiAutomatorView.setLayoutData(new GridData(GridData.FILL_BOTH));

這邊以 Screenshot Action 做範例解說,當 ScreenshotAction 被觸發後,會執行 Action 介面中定義的 run 方法,可以看到下方的程式中,透過 UiAutomatorHelper 進行畫面擷取,並且從回傳的拿出資訊透過 setModel 設回給 UiAutomatorViewer,其中 model 為給前端使用的邏輯模型,而 uiHierarchy 為模型的 XML 檔案, screenshot 則為螢幕截圖。

if (!DebugBridge.isInitialized()) {
      MessageDialog.openError(mViewer.getShell(),
              "Error obtaining Device Screenshot",
              "Unable to connect to adb. Check if adb is installed correctly.");
      return;
  }
  final IDevice device = pickDevice();
  if (device == null) {
      return;
  }
  
ProgressMonitorDialog dialog = new ProgressMonitorDialog(mViewer.getShell());
try {
    dialog.run(true, false, new IRunnableWithProgress() {
        @Override
        public void run(IProgressMonitor monitor) throws InvocationTargetException,
                                                                InterruptedException {
            UiAutomatorResult result = null;
            try {
                result = UiAutomatorHelper.takeSnapshot(device, monitor, mCompressed);
            } catch (UiAutomatorException e) {
                monitor.done();
                showError(e.getMessage(), e);
                return;
            }
            mViewer.setModel(result.model, result.uiHierarchy, result.screenshot);
            monitor.done();
        }
    });
} catch (Exception e) {
    showError("Unexpected error while obtaining UI hierarchy", e);
}

深入 UiAutomatorHelper 後會發現一些與 Android 系統的互動,在 getUiHierarchyFile() 這個方法中,可以直接取用 IDevice 類別執行一些 shell command,或是擷取畫面、取用 SyncService 來 pull/push 檔案...等等的操作,這就是引入 ddmlib 的目的。

device.executeShellCommand(command,
        new CollectingOutputReceiver(commandCompleteLatch));
device.getSyncService().pullFile(UIDUMP_DEVICE_PATH,
        dst.getAbsolutePath(), SyncService.getNullProgressMonitor());
try {
    rawImage = device.getScreenshot();
} catch (Exception e) {
    String msg = "Error taking device screenshot: " + e.getMessage();
    throw new UiAutomatorException(msg, e);
}

No comments:

Post a Comment