日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android工具HierarchyViewer 代码导读(3) -- 后台代码

發(fā)布時間:2023/12/2 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android工具HierarchyViewer 代码导读(3) -- 后台代码 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在上文中,我們講解了如何把HierarchyViewer的項目導入到Eclipse中,以便更高效閱讀代碼。本文將講解HierarchyViewer的后臺代碼,建議大家可以先閱讀<Android工具HierarchyViewer代碼導讀(1) -- 功能實現(xiàn)演示>一文, 其中的代碼演示了HierarchyViewer的主要功能。而本文就是講解HierarchyViewer是如何實現(xiàn)功能的。

?

把復雜的代碼講解清楚一般都不是很容易的事情,為了不把本文寫成流水帳,文章將盡量集中在HierarchyViewer后臺代碼的主要脈絡上,許多細節(jié)需要讀者自己去閱讀,那是必須的。

?

MVC模式

HierarchyViewer采用典型的MVC模式設計。

當打開HierarchyViewer,進入主界面時,其對應的MVC模式是:HierarchyViewerDirector.java是Controller,DeviceSelectionModel.java是Model,DeviceSelector是View,如下圖所示:

?

當雙擊某個Acitivity,進入瀏覽層次圖界面時,其對應的MVC模式是:HierarchyViewerDirector.java是Controller,TreeViewModel.java是Model,Views是TreeViewController.java、TreeViewOverview.java、PropertyViewer.java、TreeViewer.java、LayoutViewer.java:

?

HierachyViewerDirector.java(即Controller)通過DeviceBridge.java來和Android設備通信,而DeviceBridge.java具體是通過AndroidDebugBridage.java和DeviceConnection.java來和設備通信。如下圖所示:

?

AndroidDebugBridge.java : AndroidDebugBridge.java是ADB API,位于ddmlib項目中。 它實現(xiàn)了命令行版adb一樣的功能,在HierarchyViewer中主要用到其連接設備,forward端口,啟動ViewServer等操作。

DeviceConnection.java: 負責和ViewServer通信,向ViewServer發(fā)送命令并接受其返回的信息。從而獲取Activity列表、控件層次結(jié)構(gòu)圖、截圖等。

?

入口點

后臺代碼的入口點在HierarchyViewerApplication.java的createContents method中:

@Overrideprotected Control createContents(Composite parent) {// create this only once the window is opened to please SWT on MacmDirector = HierarchyViewerApplicationDirector.createDirector();mDirector.initDebugBridge();mDirector.startListenForDevices();mDirector.populateDeviceSelectionModel();//... ...}

以上代碼做了如下工作:

1,HierarchyViewerApplicationDirector.createDirector() -- 創(chuàng)建一個HierarchyViewerDirector對象

2,mDirector.initDebugBridge() -- 初始化AndroidDebugBridge

3,mDirector.startListenForDevices() -- 把mDirctor注冊為AndroidDebugBridge的監(jiān)聽者(HierarchyViewerDirector繼承了IDeviceChangeListener接口),當有設備連接、斷開、改變時,mDirctor將接收到事件。

4,mDirector.populateDeviceSelectionModel() -- 獲取當前已經(jīng)連接的設備列表,處理并顯示它們。

?

閱讀populateDeviceSelectionModel()函數(shù)你會發(fā)現(xiàn), 其中獲取到當前已經(jīng)連接的所有設備列表后,是通過deviceConnected函數(shù)來“處理”這些設備;當有新設備連接觸發(fā)設備連接事件時,也是通過deviceConnected函數(shù)來“處理”它。

?

啟動并連接設備的ViewServer,獲取Activities并顯示列表

HierarchyViewerDirector的deviceConnected 方法,是對IDeviceChangeListener接口方法的實現(xiàn),我們來看它是如何“處理”一臺和adb建立連接的設備的:

public void deviceConnected(final IDevice device) {executeInBackground("Connecting device", new Runnable() {public void run() {if (DeviceSelectionModel.getModel().containsDevice(device)) {windowsChanged(device);} else if (device.isOnline()) {DeviceBridge.setupDeviceForward(device);if (!DeviceBridge.isViewServerRunning(device)) {if (!DeviceBridge.startViewServer(device)) {// Let's do something interesting here... Try again// in 2 seconds.try {Thread.sleep(2000);} catch (InterruptedException e) {}if (!DeviceBridge.startViewServer(device)) {Log.e(TAG, "Unable to debug device " + device);DeviceBridge.removeDeviceForward(device);} else {loadViewServerInfoAndWindows(device);}return;}}loadViewServerInfoAndWindows(device);}}});}

在這個方法中做了如下事情:

1)DeviceBridge.setupDeviceForward(device) -- 把該設備的4939端口映射到本地端口。 HierarchyViewer維護一個列表 --sDevicePortMap,它記錄哪個設備被映射到了哪個本地端口。

2)DeviceBridge.isViewServerRunning(device) -- 判斷該設備的ViewServer是否打開。

3)DeviceBridge.startViewServer(device) -- 打開ViewServer。

4)loadViewServerInfoAndWindows(device) -- 1)獲取該設備ViewServer信息,比如版本信息等 2)獲取該設備其所有活動的Activities(在HierarchyView源代碼中,Activities總是被命名為Windows)。

(如果讀者不明白以上函數(shù)的意義,再次建議閱讀<功能實現(xiàn)演示>)

?

讓我們"Step Into”,來看看loadViewServerInfoAndWindows方法:

private void loadViewServerInfoAndWindows(final IDevice device) {ViewServerInfo viewServerInfo = DeviceBridge.loadViewServerInfo(device);if (viewServerInfo == null) {return;}Window[] windows = DeviceBridge.loadWindows(device);DeviceSelectionModel.getModel().addDevice(device, windows, viewServerInfo);if (viewServerInfo.protocolVersion >= 3) {WindowUpdater.startListenForWindowChanges(HierarchyViewerDirector.this, device);focusChanged(device);}}

?

1,DeviceBridge.loadViewServerInfo(device) -- 讀取ViewServer信息。

2,DeviceBridge.loadWindows(device) -- 發(fā)送 “LIST”命令給ViewServer,讀取設備所有活動的Activities。

3,DeviceSelectionModel.getModel().addDevice(device, windows, viewServerInfo) -- 更新DeviceSelectionModel數(shù)據(jù),然后該Model將通過事件通知Views來更新顯示。

?

我們到哪了?

在以上代碼完成后,HierarchyViewer完成了主界面的加載,已經(jīng)連接的設備及其活動的Activities顯示出來了:

讀取Activity的控件層次圖

這時,當用戶雙擊上圖中設備的某個Activity,希望查看其控件層次圖時,事件(DeviceSelector.java中的widgetDefaultSelected事件)將調(diào)用HierarchyViewerDirector.java的loadViewTreeData方法:

public void loadViewTreeData(final Window window) {executeInBackground("Loading view hierarchy", new Runnable() {public void run() {mFilterText = ""; //$NON-NLS-1$ViewNode viewNode = DeviceBridge.loadWindowData(window);if (viewNode != null) {DeviceBridge.loadProfileData(window, viewNode);viewNode.setViewCount();TreeViewModel.getModel().setData(window, viewNode);}}});}

?

1,DeviceBridge.loadWindowData(window) -- 讀取Activity的所有控件信息,并把每個控件的信息構(gòu)造成一個ViewNode對象,所有的ViewNode組成一個樹,該函數(shù)的返回值是樹的根節(jié)點。

2,DeviceBridge.loadProfileData(window, viewNode) -- 遍歷整個ViewNode樹,為樹中的每個節(jié)點向ViewServer讀取ProfileData。遺憾的是,目前為止我也沒有搞明白ProfileData的作用。

3,viewNode.setViewCount() -- 遍歷整個ViewNode樹,計算每個子樹所包含的節(jié)點數(shù)量,保存在ViewNode的viewCount字段中。

4,TreeViewModel.getModel().setData(window, viewNode) -- 更新TreeViewModel的數(shù)據(jù)源,該Modell將通知所有監(jiān)聽者 -- TreeViewController.java、TreeViewOverview.java、PropertyViewer.java、TreeViewer.java、LayoutViewer.java來更新視圖。

?

讀者可以“Step into” loadWindowData方法,可以看到它是通過向ViewServer發(fā)送”DUMP”命令來獲取整個控件樹信息的。

正如我們在《功能實現(xiàn)演示》中講到的,ViewServer返回給我們的控件樹信息是一個內(nèi)容巨大的文本,HierarchyViewer怎么把這個文本解析成ViewNode樹的,而TreeViewer.java,LayoutViewer.java等視圖又是如何根據(jù)ViewNode來進行繪制的,我們將是下文《前臺代碼》中講解。

?

我們到哪了?

現(xiàn)在,我們獲取到了該Activity的控件樹,并且各個Views – TreeViewer.java、LayoutViewer.java等根據(jù)ViewNode樹完成了繪制:

?

加載控件截圖

這時,當用戶選中hierarchy view(TreeView.java)上的某個節(jié)點時,HierarchyViewer將向ViewServer請求該控件的截圖,并顯示在該節(jié)點上面的氣泡中,這是怎么做到的呢?

當點擊hierarchy view上的節(jié)點時,TreeView.java上的selectionChanged方法(override ITreeChangeListener接口)被觸發(fā)(該事件的觸發(fā)過程可能要到下文<前臺代碼>中才能說清楚), 它將調(diào)用HierarchyViewerDirector.java的loadCaptureInBackground方法:

public void loadCaptureInBackground(final ViewNode viewNode) {executeInBackground("Capturing node", new Runnable() {public void run() {loadCapture(viewNode);}});}

?

讓我們“Step into” loadCapture方法:

public Image loadCapture(ViewNode viewNode) {final Image image = DeviceBridge.loadCapture(viewNode.window, viewNode);if (image != null) {viewNode.image = image;// Force the layout viewer to redraw.TreeViewModel.getModel().notifySelectionChanged();}return image;}

?

DeviceBridge.loadCapture(viewNode.window, viewNode) -- DeviceConnection.java向ViewServer發(fā)送"CAPTURE”命令來獲取控件截圖

viewNode.image = image --把截圖保存在viewNode中,下次再次選中節(jié)點時,就不用再向ViewServer請求了

TreeViewModel.getModel().notifySelectionChanged() -- 強制TreeViewModel向監(jiān)聽者發(fā)送SelectionChanged事件。

?

我們到哪了?

獲取到控件截圖后,TreeViewModel通知hierarchy view進行更新,于是我們看到截圖在氣泡中顯示出來:

?

總結(jié)語

我們試圖理清HierarchyViewer后臺代碼的主要脈絡,同時我們似乎也“遺漏”了更多內(nèi)容:我們沒有閱讀DeviceBridge.java看它都支持哪些ViewServer命令 -- 我們已經(jīng)知道的有LIST、DUMP、CAPTURE;我們沒有深入閱讀AndroidDebugBridge.java是如何工作的(也許不久后我就會寫這方面的文章);我們也沒有閱讀當設備斷開、改變時,當進行刷新等操作時的代碼。 我想我不能剝奪大家自己去閱讀代碼的樂趣。

?

本系列的最后一篇,我們將閱讀HierarchyViewer的前臺代碼。

?

本文由知平軟件的劉斌華原創(chuàng),轉(zhuǎn)載請注明出處。

知平軟件致力于移動平臺自動化測試技術(shù)的研究,我們希望通過向社區(qū)貢獻知識和開源項目,來促進行業(yè)和自身的發(fā)展。

轉(zhuǎn)載于:https://www.cnblogs.com/vowei/archive/2012/08/08/2627614.html

總結(jié)

以上是生活随笔為你收集整理的Android工具HierarchyViewer 代码导读(3) -- 后台代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。