Android自動化的一般方法
背景
Android自動化包含自動化測試和第三方App的自動化運行,這裡的自動化測試主要指的是純粹的黑盒測試,即在完全不了解代碼邏輯的情況下編寫的測試用例,可以代替人工完成重複性的工作,提高效率;而第三方App的自動化指的是為完成某一目標或獲取指定內容而進行的自動化運行。隨著技術的不斷發展,App安全性的不斷提升,破解App或者對App完成抓包的成本越來越高,通過自動化,能夠在不破解第三方App的情況下獲取App中的部分信息。除此,還可以通過自動化實現一些小工具,比如,微信自動搶紅包插件(作者已將代碼開源),跳過apk授權頁(源碼見下文)等。
自動化需要模擬用戶行為,按照事先設計好的路徑,完成固定的流程,用戶行為可能包含啟動App、點擊、滾動、輸入、返回等常規操作,一個完整的流程是這些事件的有序組合。為了使自動化程序更加穩定,即在程序進入異常或錯誤的頁面時仍然能夠正常響應,並逐步回到正常流程,事件也可能是基於頁面當前狀態的。一個基本的運行順序應該是,獲取頁面狀態,即頁面名稱、頁面內容、頁面結構,保存相應狀態,如計數器、輸出信息等,然後根據頁面狀態和保存狀態進行相應的操作,然後觸發頁面變化,再獲取新的狀態,重複這一過程。自動化工具一:輔助服務
Android系統中運行的每個App都是一個單獨進程,因而除了攻破Android系統,否則不可能在運行時獲取其他App的運行時內存,進而不能夠直接操作其他App。然而,Android系統提供了輔助服務(AccessibilityService),輔助服務是Android系統提供的一種設計用來幫助殘疾人的工具。作為Android系統提供的一種標準服務,輔助服務能夠監控Android系統中其他App中發生的事件,並可以獲取頁面結構和操作頁面,還可以進一步進行語音提示,從而幫助殘疾人更好的使用Android應用。能夠被監控的事件基本覆蓋了App內部發生的所有事件,如頁面的重繪、點擊、滾動、焦點變化等。下面藉助輔助服務來完成一個能夠自動跳過授權頁安裝apk的小工具,以說明輔助服務的使用方式。自動授權apk安裝小工具
安裝apk時,會首先彈出授權頁,這個時候需要用戶點擊安裝按鈕才能夠開始安裝。在需要同時安裝多個apk的時候,授權過程會顯得略為繁瑣。因此,可以通過一個在授權頁自動點擊安裝的小工具來完成安裝。首先通過以下代碼來向系統提交安裝apk的請求:public void autoInstall(View view) { String apkPath = Environment.getExternalStorageDirectory() + "/test.apk"; Uri uri = Uri.fromFile(new File(apkPath)); Intent localIntent = new Intent(Intent.ACTION_VIEW); localIntent.setDataAndType(uri, "application/vnd.android.package-archive"); startActivity(localIntent);}
這裡使用了一個test.apk,放到了SD卡的根目錄下,然後在代碼中獲取apk路徑,轉換成URI,設置參數application/vnd.android.package-archive
,啟動安裝程序。Android系統安裝apk的過程是由com.android.packageinstaller
這個包來管理的,所以需要監控來自這個包的所有事件。輔助服務可以指定要監控App的包名,如下為實現這個小工具的服務配置,需要放到一個單獨的xml文件中:
<?xml version="1.0" encoding="utf-8"?> <!-- res/xml/config.xml --> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChange" android:accessibilityFeedbackType="feedbackAllMask" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" android:description="@string/auto_install_desc" android:packageNames="com.android.packageinstaller" />
輔助服務反饋的事件有很多種,這裡只選擇其中兩種,獲取的事件是typeWindowStateChanged
和typeWindowContentChanged
,當窗體狀態或窗體內容變化,也就是頁面發生變化的時候,當前輔助服務會收到事件。android:description
指定的內容會在開啟輔助服務的時候,展示在輔助服務設置頁面。這個配置需要在AndroidManifest.xml
中引入:
<!-- AndroidManifest.xml --> <service android:name=".AutoInstallAccessibility" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/config"/></service>
輔助服務在 AndroidManifest.xml 中的聲明需要包含 android.permission.BIND_ACCESSIBILITY_SERVICE 許可權、過濾器和特定的meta-data,配置的xml在meta-data中的 android:resource 中引入。然後定義一個服務,繼承AccessibilityService,這個是所有輔助服務的基類,所有的輔助服務均由系統管理,這裡只需要實現特定的介面:
public class AutoInstallAccessibility extends AccessibilityService { private static final String TAG = "MyAccessibility"; @Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByText("安裝"); if (nodes != null && nodes.size() > 0) { for (AccessibilityNodeInfo node : nodes) { node.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } } } @Override public void onInterrupt() { }}
自定義服務需要實現
onAccessibilityEvent(AccessibilityEvent event)
來處理監控的App反饋的事件,通過
getRootInActiveWindow()
來獲取頁面的根節點信息,這個根節點信息是一個
AccessibilityNodeInfo
的實例,可以通過其獲得節點及子節點的狀態信息,如展示的文字getText()、描述欄位getContentDescription(),同時也可以通過這些信息定位子節點。節點的點擊操作通過performAction這個函數完成。最後,啟動系統的輔助服務列表頁:
public void startAccessibilityConfigPage(View view) { Intent accessibleIntent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); startActivity(accessibleIntent);}
所有的輔助服務都會展示在這個列表頁中,在輔助服務設置頁面中,找到當前應用,進入輔助服務配置頁,點擊開啟服務。之後啟動安裝即可完成自動授權過程。在不同Android設備上可能表現不同,這裡的邏輯是:監控系統安裝頁面的事件,然後在頁面中找到帶有安裝文案的節點,執行點擊操作。不同設備安裝頁面的頁面結構可能不盡相同,要想兼容各種各樣的Android設備可以使用Android SDK提供的層級查看工具uiautomatorviewer來分析App的頁面結構,然後確定搜索和點擊路徑,如英文系統可能點擊的文案是INSTALL
- 輔助服務本身是一個Service,這個Service被系統管理,可能因為很多原因掛掉,則自動化停止。
- 輔助服務無法自動化WebView。
自動化工具二:uiautomator
輔助服務作為Android系統中的一種標準服務,很難通過腳本去配置和控制,也不容易監控。另外一個進行自動化的思路是通過自動化測試工具,這要求自動化測試工具必須是完全黑盒的,也即在不了解代碼邏輯、代碼介面的前提下,能夠順利的編寫用例,實現必要的功能。uiautomator是Google提供的為Android編寫UI測試用例的自動化工具,開發時,可以按照如下方式使用Gradle添加依賴:androidTestCompile com.android.support.test.uiautomator:uiautomator-v18:2.1.3
也可以直接依賴uiautomator.jar,uiautomator.jar在Android SDK目錄下的platforms/[android-version]/
目錄中,提供編寫用例所需要的工具類。運行用例必須在Android系統中,可以打成Java的可執行文件jar包,也可以打包成Android標準的test apk,執行需要依賴ADB工具。除此,uiautomator還可以作為獲取Android設備頁面布局的工具,Android SDK目錄tools/bin/下的可執行文件uiautomatorviewer是uiautomator獲取首頁頁面布局的圖形界面版本,能夠方便的查看頁面布局,在分析第三方App的時候,會經常用到。
adb shell uiautomator help
adb的help命令會詳細輸出每個指令的具體功能。這裡首先使用dump命令,通過腳本,實現基本的自動化功能。dump命令會把當前Android設備展示的頁面結構以xml的方式輸出到文件,所以需要做的是解析xml,獲取各個節點信息,節點信息會包含位置,之後配合adb的input指令,完成基本的操作,實現自動化。下面利用dump命令完成簡單的自動化腳本,腳本使用Python編寫。首先通過adb命令獲取頁面布局:
def execute_adb(cmd): return subprocess.check_output((adb %s % cmd).split( ))def dump_layout(): """生成的默認文件默認保存在/sdcard/window_dump.xml,通過pull命令取回本地""" execute_adb(shell uiautomator dump) execute_adb(pull /sdcard/window_dump.xml .)
頁面布局的xml文件會把Android設備的頁面解析成一個由node節點構成的樹結構,每個node節點會固定的包含位置、點擊、View的類名等信息,如下是一個根節點的信息:
<node bounds="[0,0][1080,1794]" checkable="false" checked="false" class="android.widget.FrameLayout" clickable="false" content-desc="" enabled="true" focusable="false" focused="false" index="0" long-clickable="false" package="com.example.layout" password="false" resource-id="" scrollable="false" selected="false" text="">
節點信息包含了開發中常會用到的屬性,由此可以看到,雖然限於Android內存管理模型無法獲取頁面上節點的實際內存對象,但是仍然可以藉助uiautomator來獲取節點的狀態。bounds是當前節點在屏幕中的位置,為了操作節點,需要使用位置信息確定指定View的位置,然後通過adb的input命令完成具體的操作。解析xml,獲取節點位置,完成點擊、滾動、輸入等操作:
def read_bounds(text): """獲取指定文字內容的節點位置""" pattern = re.compile(r[(d+),(d+)][(d+),(d+)]) with open(./window_dump.xml, r) as f: file_content = f.read() if file_content: node_file = etree.XML(file_content)bounds_str = node_file.xpath(u//node[@text="%s"]/@bounds % text)[0] return map(int, pattern.match(bounds_str).groups()) return Nonedef perform_click(x, y): execute_adb(shell input tap %d %d % (x, y))def perform_scroll(x0, y0, x1, y1, duration): execute_adb(shell input swipe %d %d %d %d %d % (x0, y0, x1, y1, duration))def input_text(text): execute_adb(shell input text %s % text)......
adb的input的命令十分豐富,能夠滿足絕大多數的交互操作,tap模擬點擊,swipe模擬手指在屏幕上移動,text模擬文字輸入,除此比較常用到的還有keyevent,用於模擬按鍵操作,按鍵包含了返回鍵、home鍵、軟鍵盤以及外接鍵盤的按鍵。如果需要支持多設備,需要為adb命令加上-s選項,指定設備序列號。通過拼接這些操作,能夠完成簡單的自動化程序。
利用dump方式的缺陷- dump指令耗時過長,嚴重影響自動化效率。
用例方式
uiautomator的主要功能是用來執行測試用例,測試用例使用Java編寫,開發時依賴Android SDK。可以通過執行用例的方式來進行自動化操作第三方App,用例一般的開發方式是編寫用例函數,打包成jar或者test apk。寫好的用例不能在本地直接運行,如果是jar包,必須推送到Android設備上通過adb運行,如果是test apk,需安裝運行。test apk是指用來執行測試的apk,會在build的時候生成,如下為SDK中uiautomator工具API中UiDevice的部分源碼,可以看到源碼沒有實現,只是提供了介面,因此用例無法脫離設備運行。UiDevice() { throw new RuntimeException("Stub!");}public void setCompressedLayoutHeirarchy(boolean compressed) { throw new RuntimeException("Stub!");}......
uiautomator提供了獲取設備和頁面節點信息的API,藉助UiDevice來獲取設備信息,藉助UiObject、UiObject2獲取頁面上的節點信息。
通過用例方式完成自動化程序的步驟為:- 分析要自動化的App的頁面結構,設計自動化路徑
- 編寫自動化程序(實際上是一個測試用例)
- 打包,push到Android設備上(apk通過adb am instrument命令,jar包通過adb shell uiautomator runtest命令運行)
- 執行自動化程序
使用這種方式執行自動化效率相比dump指令方式明顯有所提高,如下為通過這種方式自動獲取當前用戶已關注微信公眾號列表的程序,微信使用6.6.5版本:
@RunWith(AndroidJUnit4.class)public class WeixinAutomator { @Test public void listWxPublicAccount() throws UiObjectNotFoundException { UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); Context context = InstrumentationRegistry.getContext(); final String logTag = "listWxPublicAccount"; device.pressHome(); Intent launchIntent = new Intent(); launchIntent.setComponent(new ComponentName("com.tencent.mm", "com.tencent.mm.ui.LauncherUI")); context.startActivity(launchIntent); device.findObject(new UiSelector().text("通訊錄")).click(); device.findObject(new UiSelector().text("公眾號")).click(); UiObject listObj = device.findObject(new UiSelector().className("android.widget.ListView")); HashSet<String> names = new HashSet<>(); while (true) { int childCount = listObj.getChildCount(); for (int i = 0; i < childCount - 1; i++) {String text = listObj .getChild(new UiSelector() .index(i) .childSelector(new UiSelector() .className("android.widget.TextView") .resourceId("com.tencent.mm:id/z1"))) .getText(); if (!names.contains(text)) { names.add(text); Log.e(logTag, "text: " + text); } } try { UiObject obj = device.findObject(new UiSelector().textContains("個公眾號")); if (obj != null && !TextUtils.isEmpty(obj.getText())) { break; } } catch (UiObjectNotFoundException e) { } UiScrollable listScrollable = new UiScrollable(new UiSelector().scrollable(true)); listScrollable.scrollToEnd(1); }}}
運行uiautomator的用例需要使用AndroidJUnit4,用例的入口方法要用org.junit.Test這個標籤修飾,多個用例之間執行順序無法保證,所以不能通過多用例的方式進行自動化。這裡首先啟動微信的首頁,然後通過文案查找的方式進入公眾號列表頁面,遍歷公眾號條目,通過uiautomatorviewer可以看到公眾號名稱對應的resourceId是com.tencent.mm:id/z1(不同版本、渠道的微信apk可能對應的resourceId是不一樣的,因為微信使用AndResGuard進行了資源混淆),通過UiSelector獲取節點,然後通過getText獲取名稱,之後進行滾動。遍歷的終止條件是:當公眾號列表頁面滾動到底部之後,一定會展示「xx個公眾號」這樣的文案,檢測到這個文案,認為列表已經滾到底部。UiObject的對象是長時間有效的,所以當執行滾動之後,使用listObj對象仍然可以獲取到當前ListView的最新節點數量。
最後,打包執行。通過adb命令運行:
# jar包的運行方式adb shell uiautomator runtest case.jar -c com.example.WeixinAutomator# test apk的運行方式## 首先push test apk到Android設備上adb push [test-apk-path] /data/local/tmp/com.example ## 安裝adb shell pm install -t -r "/data/local/tmp/com.example" ## 啟動測試用例adb shell am instrument -w -e class com.example.WeixinAutomator com.example.test/android.support.test.runner.AndroidJUnitRunnerruntest
runtest命令通過-c指定要執行用例的主類路徑。instrument命令用來執行Android的InstrumentationTest,-w選項表明等待直到用例運行結束,-e表示添加key-value形式的參數,設置用例的入口為com.example.WeixinAutomator,即用例的class路徑,最後是用來執行用例的AndroidJUnitRunner的類路徑。
用例方式實現自動化的缺陷- 無法自動化WebView。
集成adb
ADB是Google提供的能夠和Android設備進行交互的命令行工具。如上所示的小程序均是基於順序的,各個事件按照事先設計好的順序,一個一個執行,意味著在執行過程中,如果不出現意外,能夠正常運行,但如果出現諸如斷網、網速過慢、手機卡頓、app崩潰之類的問題後,自動化就會異常停止。所以自動化想要穩定運行,應該是基於頁面狀態的。基於頁面狀態是指,任何一個頁面,不論其當前展示的內容是什麼,是斷網還是未斷網,是否載入失敗,都應對應於一種狀態,程序應該能夠處理這種狀態,從而即使偏離了原有的流程,也能夠慢慢的再回到正常的流程。這就要求完整的監控機制。除此,自動化通常運行獨立,不會有人工實時的監測,也即出現問題,應該能夠事後發現和修復問題。這就要求要有完整的日誌系統。要想實現這些,需要依賴adb。同時,不論是輔助服務還是uiautomator,雖然能一定程度的解析WebView的結構,但都無法操作WebView。通過adb的input指令,可以模擬用戶的各種操作,這也使得自動化WebView成為可能。
由於大多數未root的Android設備都無法進行遠端adb連接,也即無法通過遠程的方式執行adb命令,所以這裡考慮使用套接字。在宿主設備建立套接字服務使用adb調試Android設備通常需要使用PC或者伺服器,這裡暫且把其稱作宿主設備。在宿主設備上建立套接字服務,則請求方為Android設備。宿主設備輔助完成自動化功能,並實時監控Android設備的自動化狀態。如果要確保在Android設備上運行的程序能夠穩定運行,應設立超時機制,如果超時未收到來自Android設備的事件信息,則應進行相應的恢復或重啟操作。
Android設備本身是一個Linux系統,若想要在這個Linux系統和宿主設備操作系統之間建立套接字通信,首先要進行埠轉發。在宿主設備上建立套接字服務,Android設備向宿主設備發送請求,則需要把Android設備上的某一空閑埠上的內容映射到宿主的某一空閑埠上,如把Android設備的8484介面轉發到宿主設備的8484埠上,需要執行:adb reverse tcp:8484 tcp:8484
可以使用:
adb reverse --list
查看已經映射的埠。然後在宿主設備上建立套接字服務,這裡仍然使用Python:
import SocketServer import json import threading import tracebackPORT = 8484 HOST = "localhost" chunk_size = 4096class AndroidCommandHandler(SocketServer.BaseRequestHandler): def handle(self): try: data = self.request.recv(chunk_size) while data.find( ) < 0: data += self.request.recv(chunk_size) data = data.split( )[0] # ... # params = json.loads(data) # params是來自Android設備的請求,解析params並執行相應的操作 # ... res = {success: True} self.request.sendall(json.dumps(res) + ) except: res = {success: False} self.request.sendall(json.dumps(res) + ) traceback.print_exc()class AndroidCommandServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): passserver = AndroidCommandServer((HOST, PORT), AndroidCommandHandler) server_thread = threading.Thread(target=server.serve_forever) server_thread.start()
這裡為了簡便,利用Python的 SocketServer.ThreadingMixIn 啟動一個多線程處理事件的套接字服務,AndroidCommandHandler 這裡的解析和處理過程會在一個單獨的線程中進行,使用雙換行作為通信的終止符,指令接收完畢可以按照一些實現定義好的協議去做相應的處理,比如執行adb操作、記錄日誌等。一種典型的應用場景是針對WebView,因為輔助服務和uiautomator都無法操作WebView,所以可以先在Android設備上通過輔助服務或uiautomator分析WebView的節點結構,然後把節點信息通過套接字發送到宿主設備上,再在宿主設備通過adb的input的命令完成具體的操作。
在Android設備上建立套接字服務在Android設備上建立套接字服務,則宿主設備作為請求方。這種方式可以通過套接字把原本依賴設備的處理過程放到宿主設備上,也即任何一次查詢和操作都是由宿主設備發起的,這樣想要進行的自動化流程完全可以在宿主設備上編寫程序來處理。開源自動化測試框架Appium即通過這種方式,能夠在完全不需要修改源碼條件下,執行自動化測試用例,並且,支持任意語言來編寫自動化測試用例。因為基於這樣的結構,任何開發語言都能使用套接字來和Android設備通信,從而獲取頁面信息、操作Android設備。Appium使用Node.js搭建,同時支持iOS和Android的自動化測試,為Android提供底層介面的是appium-android-bootstrap,通過源碼,可以看到其是依賴uiautomator實現的。不過Appium框架本質上是用來進行自動化測試的,框架功能強大,但是卻不能很好的應用於第三方apk,其主要是用於debug版本的apk的。如使用Appium嘗試自動化微信,在啟動Appium服務的時候,會產生一條錯誤信息,導致自動化停止。閱讀Appium源碼之後,發現原因是Appium在安裝apk之前會使用aapt命令來獲取apk的基本信息,而這條命令在微信的apk上執行失敗。即便如此,Appium的思路仍然是值得借鑒的,下面簡單說明Appium這種利用套接字來開放uiautomator介面的方式。首先需要把宿主設備上的某一埠內容,全部轉發到Android設備上,使用adb命令:adb forward tcp:4724 tcp:4724
由於分析的Appium的源代碼,所以使用Appium的埠號,Appium在宿主設備上的服務默認是使用4724作為和Android設備進行通信的埠。查看已經轉發的介面:
adb forward --list
adb的forward命令和reverse命令用法基本一致,只是方向是相反的,forward是把宿主設備上的埠轉發到Android設備上,reverse是把Android設備的埠轉發到宿主設備上。然後利用一個測試用例,啟動套接字服務:
// appium-bootstrap源碼public class Bootstrap extends UiAutomatorTestCase { public void testRunServer() { Find.params = getParams(); boolean disableAndroidWatchers = Boolean.parseBoolean(getParams().getString("disableAndroidWatchers")); boolean acceptSSLCerts = Boolean.parseBoolean(getParams().getString("acceptSslCerts")); SocketServer server; try { server = new SocketServer(4724); server.listenForever(disableAndroidWatchers, acceptSSLCerts); } catch (final SocketServerException e) { Logger.error(e.getError()); System.exit(1); } }}
Appium在Android設備上啟動一個套接字服務監聽來自埠4724的所有消息,套接字服務會做一個無限循環,也即是一個持續不會中斷的測試用例,這個用例會在收到來自宿主設備的命令時,執行相應的操作。然後是對於消息的處理:
// appium-bootstrap源碼public void listenForever(boolean disableAndroidWatchers, boolean acceptSSLCerts) throws SocketServerException { ... ... try { client = server.accept(); Logger.debug("Client connected"); in = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream(), "UTF-8")); while (keepListening) { handleClientData(); } in.close(); out.close(); client.close(); Logger.debug("Closed client connection"); } catch (final IOException e) { throw new SocketServerException("Error when client was trying to connect"); }}
client是一個(java.net.Socket)[https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html]對象,調用accept建立套接字連接,獲取套接字內容。內容由handleClientData來處理,handleClientData只是將字元串解析成具體的指令然後調用runCommand來執行具體的命令:
// appium-bootstrap源碼private String runCommand(final AndroidCommand cmd) { AndroidCommandResult res; if (cmd.commandType() == AndroidCommandType.SHUTDOWN) {keepListening = false; res = new AndroidCommandResult(WDStatus.SUCCESS, "OK, shutting down"); } else if (cmd.commandType() == AndroidCommandType.ACTION) { try { res = executor.execute(cmd); } catch (final NoSuchElementException e) { res = new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT, e.getMessage()); } catch (final Exception e) { Logger.debug("Command returned error:" + e); res = new AndroidCommandResult(WDStatus.UNKNOWN_ERROR, e.getMessage()); } } else { // this code should never be executed, here for future-proofing res = new AndroidCommandResult(WDStatus.UNKNOWN_ERROR, "Unknown command type, could not execute!"); } return res.toString();}
來自宿主設備的指令可以關閉當前服務,也可以執行相應的Action。Appium定義的Action有很多,節點相關的節點Action會以element:開頭,之後才是具體的Action內容,Appium以這種方式來確定該操作是否需要節點信息,Action的內容可能包括節點尋找、執行具體的點擊、滾動、輸入等等,Appium定義的所有Action如下所示:
// appium-bootstrap源碼private static HashMap<String, CommandHandler> map = new HashMap<String, CommandHandler>(); static { map.put("waitForIdle", new WaitForIdle()); map.put("clear", new Clear()); map.put("orientation", new Orientation()); map.put("swipe", new Swipe()); map.put("flick", new Flick());map.put("drag", new Drag()); map.put("pinch", new Pinch()); map.put("click", new Click()); map.put("touchLongClick", new TouchLongClick()); map.put("touchDown", new TouchDown()); map.put("touchUp", new TouchUp()); map.put("touchMove", new TouchMove()); map.put("getText", new GetText()); map.put("setText", new SetText()); map.put("getName", new GetName()); map.put("getAttribute", new GetAttribute()); map.put("getDeviceSize", new GetDeviceSize()); map.put("scrollTo", new ScrollTo()); map.put("find", new Find()); map.put("getLocation", new GetLocation()); map.put("getSize", new GetSize()); map.put("getRect", new GetRect()); map.put("wake", new Wake()); map.put("pressBack", new PressBack()); map.put("pressKeyCode", new PressKeyCode()); map.put("longPressKeyCode", new LongPressKeyCode()); map.put("takeScreenshot", new TakeScreenshot()); map.put("updateStrings", new UpdateStrings()); map.put("getDataDir", new GetDataDir()); map.put("performMultiPointerGesture", new MultiPointerGesture()); map.put("openNotification", new OpenNotification()); map.put("source", new Source()); map.put("compressedLayoutHierarchy", new CompressedLayoutHierarchy()); map.put("configurator", new ConfiguratorHandler());}
Appium使用的基本都是uiautomator的介面。Appium是支持WebView的測試用例的,只是需要App的WebView開啟Debug模式,同樣這在第三方App上是無法做到的,所以針對這種情況,仍然要使用套接字通知宿主設備之後,利用adb的input方式來進行操作。
總結
如上介紹了自動化Android App過程中可能用到的幾個工具,將這些工具搭配使用能夠互補的實現原本無法實現的功能,同時配合宿主設備,搭建完整的穩定的Android自動化系統。經過一段時間的實踐發現,通過輔助服務來獲取事件和分析節點結構的效率是最高的,uiautomator是功能和API最豐富的,而腳本和adb命令是調試和控制最方便的。通過自動化能夠做到定期監控其他App的狀態,從第三方App中獲取基本信息,而無需進行複雜繁瑣的逆向分析。同時,自動化能夠很好的解決重複性工作,節省人力,來進行更多的工作,當然,也能夠通過自動化完成對自身App的基本測試,覆蓋主要流程,然後可以用來作為上線之前的一次安全檢查。
推薦閱讀:
※人性化科技是科技發展的必由之路
※擁有1000項重大發明的特斯拉為什麼瀕臨破產?
※網易雲音樂有哪些扎心的熱評?
※人類可不可能像被圈養的動物一樣,其實一直處於另外一個高等文明的觀察之下?
※有哪些用著先進的武器裝備卻輸的一塌塗地的戰爭?