原文鏈接:http://www.cnblogs.com/by-dream/p/4921701.html#3328376
以一個簡單的例子開始吧。我們完成一個 " 打開QQ,進入QQ空間,然后退出 " 的case。
代碼如下:
package QQ; import java.io.IOException; import com.android.uiautomator.core.UiDevice; import com.android.uiautomator.core.UiObject; import com.android.uiautomator.core.UiObjectNotFoundException; import com.android.uiautomator.core.UiSelector; import com.android.uiautomator.testrunner.UiAutomatorTestCase; public class Test_qq extends UiAutomatorTestCase { public void testDemo() throws IOException, UiObjectNotFoundException { // 啟應用 Runtime.getRuntime().exec("am start com.tencent.mobileqq/com.tencent.mobileqq.activity.SplashActivity"); sleep(3000); // 點擊 "動態" tab UiDevice device = getUiDevice(); int height = device.getDisplayHeight(); int width = device.getDisplayWidth(); device.click(width -50, height-50); sleep(1000); // 點擊 "好友動態" 按鈕 UiObject obj_1 = new UiObject(new UiSelector().description("點擊進入好友動態")); obj_1.click(); sleep(2000); // 點擊 左上角返回 "動態"按鈕 UiObject obj_2 = new UiObject(new UiSelector().resourceId("com.tencent.mobileqq:id/ivTitleBtnLeft")); obj_2.click(); sleep(1000); // 點擊菜單鍵 device.pressMenu(); sleep(1000); // 點擊退出qq UiObject obj_3 = new UiObject(new UiSelector().text("退出QQ")); obj_3.click(); sleep(1000); // 點擊確定 UiObject obj_4 = new UiObject(new UiSelector().text("確定")); obj_4.click(); } }
腳本的運行效果如下:
代碼詳解
針對上面的例子的代碼,我對每一句代碼都做個詳細的解釋吧。
第一部分:啟動應用
exec() 這個函數的意思,相當於是在你在輸入adb shell 命令后,在Android手機系統的命令行下運行。所以上面這句話的意思和我們打開cmd框輸入" adb shell am start *** " 是一樣的的效果。
一般來說我們做App的自動化的時候,第一步都是把App打開,這個am start命令的就可以幫我們實現,類似與Monkeyrunner API中的startActivity() 函數。
第二部分:點擊 “動態” tab
UiDevice對象會在API部分詳細講解,它是一個我們在Uiautomator中經常使用的一個對象。
這里我們首先用它獲取到當前手機的寬和高的像素。然后觀察到 “動態” tab位於右下方,因此在取得右下角的坐標點后,又進行了一個大概的坐標變化(這里為了簡單只是向左和向上移動了50像素,如果要精確的可以進行等比轉化),然后點擊該坐標。
這里之所以用點擊坐標的方法,一方面是因為這個控件Uiautomator不支持用API獲得實例(上一節所說的NAF Nodes,如下圖),另一方面也是想說明在一些控件沒有固定的id、text和desc的時候,我們應該怎么處理。
第三部分:點擊 “好友動態”
要想操作一個控件(例如),首先得獲得一個UiObject對象,而UiObject對象可以通過UiSelector來構造,而UiSelector可以根據控件的id、text、content-desc來進行構造,這里就是用content-desc來構造。
如上圖用 uiautomatorviewer 查到該控件的 content-desc 的內容是 “點擊進入好友動態” ,因此我們就可以通過代碼中的方法來得到UiObject對象了,然后調用click() 方法來達到點擊效果。
第四部分:點擊左上角返回按鈕
同第三部分的方法,找到id后直接獲得到UiObject對象,進行點擊。
第五部分:點擊菜單鍵
UiDevice 可以模擬點擊home、back、menu 這三個鍵,代碼應該大家都懂的怎么變化了吧。
第六部分:退出
這一部分也是先通過獲取出控件屬性中的text值,然后構造出UiObject對象,完成點擊。
以上部分內容就是整個操作QQ這個小例子的全部代碼講解,看完之后對寫Uiautomator代碼有了更進一步的了解了吧。接下來寫看看還有哪些API可以支持我們做更多的事情。
API 列舉
UiDevice
概述:
UiDevice用與訪問關設備狀態的信息,也可以使用這個類來模擬用戶在設備上的操作。可以通過下面的方法得到實例:
UiDevice mdevice = getUiDevice();
摘要:
函數返回值 | 函數體 | 說明 | 實例 |
boolean | click(int x, int y) | 模擬用戶在指定位置點擊 | mdevice.click(200, 300) 點擊屏幕的200,300坐標處 |
String | getCurrentActivityName() | 獲得的是應用程序在桌面上顯示的名字 | 例如,在qq首頁得到的是“QQ”,在微信登錄頁得到的是“微信”,注意,這個得到的不是Activity的名字 |
String | getCurrentPackageName() | 獲得當前顯示的應用程序的包名 | 例如,在微信啟動的時候,獲得的是“com.tencent.mm” |
int | getDisplayHeight() | 獲得當前設備的屏幕分辨率的高 | 例如,我的手機1920*1080,得到的是 1920 |
int | getDisplayWighth() | 獲得當前設備的屏幕分辨率的寬 | 例如,我的手機1920*1080,得到的是 1080 |
boolean | isScreenOn() | 判斷手機當前是否滅屏 | 當手機滅屏的時候,得到是“false”,手機亮屏,得到的是“true” |
void | wakeUp() | 點亮當前屏幕 | 調用后,相當於按下了電源鍵,如果手機設置了滑動鎖屏,滑動鎖屏還是在的,不會自動解開 |
boolean | pressBack() | 點擊back鍵 | |
boolean | pressHome() | 點擊home鍵 | |
boolean | pressMenu() | 點擊menu鍵 | |
boolean | pressKeyCode(int keyCode) | 利用keycode值模擬一次按下事件 | 例如,需要按下數字1 數字1的keycode是 KEYCODE_NUMPAD_1,更多keycode可以在 http://developer.android.com/intl/zh-cn/reference/android/view/KeyEvent.html 進行查詢 |
boolean | swipe(int startX, int startY, int endX, int endY, int steps) | 用指定的步長,從A點滑動B點 | 例如,需要從(10, 10)點用兩步滑動到(100, 200)點,則需要mdevice.swipe(10, 10, 100, 200, 2) |
boolean | takeScreenshot(File storePath) | 截取當前屏幕,保存到文件 | 例如,File files = new File("/sdcard/res.jpg"); mdevice.takeScreenshot(files); 即可將截圖保存到sd卡中了。 |
UiSelector
概述:
按照一定的條件(例如控件的text值,資源id),定位界面上的元素。UiSelector對象的最終目的是去構造一個UiObject對象。
摘要:
1、根據text構造:
函數返回值 | 函數體 | 說明 | 用法 |
UiSelector | text(String text) | 根據“控件text屬性的內容”構造出UiSelector對象 | 例如,一個控件text的值是“發現”,UiSelector s = new UiSelector().text("發現"); |
UiSelector | textContains(String text) | 根據“控件text屬性包含的內容”構造出UiSelector對象 | 同上例子:UiSelector s = new UiSelector().textContains("現"); |
UiSelector | textMatches(String regex) | 根據“控件text屬性正則表達式的內容”構造出UiSelector對象 | 正則表達式語法參考網上資料即可。 |
UiSelector | textStartsWith(String text) | 根據“控件text屬性開始的內容”構造出UiSelector對象 | 同上例子:UiSelector s = new UiSelector().textStartsWith("發"); |
比較常用,准確度也比較高,中文查找的時候,如果遇到 “UiOjbectNotFoundException” 的時候,記得把項目的編碼格式改為utf-8。
2、根據description構造:
UiSelector | description(String desc) | 根據“控件content-desc屬性的內容”構造出UiSelector對象 |
UiSelector | descriptionContains(String desc) | 包含** |
UiSelector | descriptionMatches(String regex) | 正則 |
UiSelector | descriptionStartsWith(String desc) | 以**開始 |
同text的用法基本一致,也是比較靠譜的一種方式。
3、根據資源id:
UiSelector | resourceId(String id) | 根據資源id獲取對象,例如:UiSelector s = new UiSelector().resourceId("com.tencent.mm:id/b8m") |
UiSelector | resourceIdMatches(String regex) | 根據資源id的正則表達式獲取對象 |
4、根據類:
UiSelector className(String className):
根據控件的類名來找到UiSelector對象。
但是呢?因為一般Android布局的時候,同樣的控件類名都是一樣的。
因此我在微信的登錄界面調用: UiSelector s = new UiSelector().className("android.widget.TextView") 這句話,它得到的就是我左上開始算第一個class名稱為“android.widget.TextView”的控件。
UiSelector instance (int instance):
上面提到的假如我們想獲取屏幕上電話號碼的那個TextView使用這樣方法,就可以使用instance:
UiSelector s = new UiSelector().className("android.widget.TextView").instance(1);
UiSelector index(int index):
用法和上面的instance差不多,谷歌的原文說這個方法是unreliable的,推薦使用instance方法。
UiSelector childSelector(UiSelector selector):
有的時候假如子控件不好獲得,而其父控件比較好獲得的時候,我們通常采用這樣的方式,例如下面:
我們目前選中的是LinearLayout,這個Android中的一種布局,它的里面嵌套了兩個控件,一個是ImageView,另一個是EditText。這們這里就通過LinearLayout這個控件找到它的子控件。
很明顯,父控件id已經給定。我們先得到父控件:UiSelector s_p = new UiSelector().resourceId("com.tencent.mm:id/axj");
其次 UiSelector s_c= s_p.childSelector( new UiSelector().className("android.widget.EditText") );
在它的父控件的childSelector方法中傳入一個帶有一定特征的UiSelector對象,即可得到子控件,這里 s_c 就是輸入框的UiSelector對象。
UiSelector fromParent(UiSelector selector):
有的時候父控件也不好獲得,而是同級的控件(同屬一個parent)比較好獲取,那么使用這樣方法,還拿上面的舉例:
我們先得到EditText的UiSelector對象:UiSelector s1 = new UiSelector().resourceId("com.tencent.mm:id/axc");
得到和它同樣一個父控件的ImageView的UiSelector對象:UiSelector s2 = fromParent( new UiSelector().className("android.widget.ImageView") );
5、根據特有屬性:
UiSelector | checked(boolean val) | 根據是否可check來構造出UiSelector對象 |
UiSelector | chickable(boolean val) | |
UiSelector | enabled(boolean val) | |
UiSelector | focusable(boolean val) | |
UiSelector | longClickable(boolean val) | |
UiSelector | scrollable(boolean val) | |
UiSelector | selected(boolean val) |
舉個簡單的例如,假如當前的界面,只有一個checkbox是勾選狀態,你就可以這樣得到:UiSelector s2 = new UiSelector().checked(true)
UiCollection
概述: 用的不多,直接參考文檔
摘要: http://android.toolib.NET/tools/help/uiautomator/UiCollection.html
UiScrollable
概述: 用的不多,直接參考文檔
摘要: http://android.toolib.net/tools/help/uiautomator/UiScrollable.html
UiObject
概述:可以理解為 直接操作界面ui元素的實例。
摘要:
返回值 |
函數 |
void |
ClearTextField() |
boolean |
click() |
boolean |
clickAndWaiForNewWindow(long timeout) |
boolean |
clickAndWaiForNewWindow() |
boolean |
clickBottomRight() |
boolean |
clickTopLeft() |
boolean |
exists() |
Rect |
getBounds() |
UiObject |
getChild(UiSelector selector) |
int |
getChildCount() |
String |
getContentDescription() |
UiObject |
getFromParent(UiSelector selector) |
String |
getPackageName() |
final UiSelector |
getSelector() |
String |
getText() |
Rect |
getVisibleBounds() |
boolean |
isCheckable() |
boolean |
isChecked() |
boolean |
isClickable() |
boolean |
isEnabled() |
boolean |
|
boolean |
isFocused() |
boolean |
isLongClickable() |
boolean |
isScrollable() |
boolean |
|
boolean |
|
boolean |
|
boolean |
|
boolean |
|
boolean |
|
boolean |
|
boolean |
|
boolean |
|
boolean |
|
boolean |