在Android P出來後,開展自動化測試的路上一些地方有些磕磕絆絆。先說說之前發現的一種必現的無法點擊到桌面控件的問題,明明界面中存在,但是無法被點擊,Uiautomator運行時並沒有拋出這一步的異常,但是下一步就拋異常了。
詳細的實例如下:case刪除全部聯繫人的操作步驟(點擊刪除--選擇確定--驗證是否正確刪除)
而因爲無法點擊到刪除這個控件,導致沒有彈出確認窗口,導致下一步的點擊“確定”按鈕這一步拋UiObjectNotFound異常。
@Test
public void testCase() throws UiObjectNotFoundException {
UiObject deleteBtn=new UiObject(new UiSelector().text("刪除"));//未正確點擊,但未拋出異常
deleteBtn.click();
UiObject okBtn=new UiObject(new UiSelector().text("確定"));//拋異常
okBtn.click();
}
那麼,接下來,分析下,爲什麼出錯了。使用UiautomatorViewer同步快照後,發現刪除按鈕被截取掉一部分了。
可見,控件確實存在,但是其中有一部分被幹掉了,整個屏幕被切割掉了一小部分。
從Uiautomator源碼中,我們可以知道,Uiautomator其實獲取控件後,會獲取該控件的中心點座標,隨後點擊座標,從而實現點擊控件。上源碼:
UiObject類的click()方法:
/**
* Performs a click at the center of the visible bounds of the UI element represented
* by this UiObject.
*
* @return true id successful else false
* @throws UiObjectNotFoundException
* @since API Level 16
*/
public boolean click() throws UiObjectNotFoundException {
Tracer.trace();
AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
if(node == null) {
throw new UiObjectNotFoundException(mUiSelector.toString());
}
Rect rect = getVisibleBounds(node);
return getInteractionController().clickAndSync(rect.centerX(), rect.centerY(),
mConfig.getActionAcknowledgmentTimeout());//rect.centerX(), rect.centerY()。中心點
}
跳轉到etInteractionController().clickAndSync()方法中:
/**
* Click at coordinates and blocks until either accessibility event TYPE_WINDOW_CONTENT_CHANGED
* or TYPE_VIEW_SELECTED are received.
*
* @param x
* @param y
* @param timeout waiting for event
* @return true if events are received, else false if timeout.
*/
public boolean clickAndSync(final int x, final int y, long timeout) {
String logString = String.format("clickAndSync(%d, %d)", x, y);
Log.d(LOG_TAG, logString);
return runAndWaitForEvents(clickRunnable(x, y), new WaitForAnyEventPredicate(
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED |
AccessibilityEvent.TYPE_VIEW_SELECTED), timeout) != null;
}
不確定是開發全面屏機器都這樣還是部分ROM的開發者不小心留的坑,但,無力吐槽,更無力修復這種軟件狀態,只能尋找解決方案。
多方嘗試後,發現adb shell input tap <x> <y> 可以實現屏幕點擊,而滑動(長按)則可以通過adb shell input swipe <x1> <y1> <x2> <y2> [duration(ms)]來實現。而且,上面的切割部分的座標,也是可以點擊到的。辣麼,解決方案便出來了。只要修改下點擊的方式更換爲Runtime.getRuntime().exec("input tap "+rect.centerX()+" "+ rect.centerY()),可以是在源碼上做修改,也可以是在自己二次封裝的代碼上替換掉就可以了。貼上adb shell input 幫助文檔:
C:\Users>adb shell input
Usage: input [<source>] <command> [<arg>...]
The sources are:
dpad
keyboard
mouse
touchpad
gamepad
touchnavigation
joystick
touchscreen
stylus
trackball
The commands and default sources are:
text <string> (Default: touchscreen)
keyevent [--longpress] <key code number or name> ... (Default: keyboard)
tap <x> <y> (Default: touchscreen)
swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
draganddrop <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
press (Default: trackball)
roll <dx> <dy> (Default: trackball)