python uiautomator2 toast_浅谈UiAutomator2之Toast那些事

背景

Toast是Android开发里面十分常用的显示用户提示信息的UI组件,但其往往是闪瞬即逝的,在界面中停留的时间非常短暂。而我们的某些自动化案例中确实是需要验证Toast显示的内容是否正确的,例如蓝牙配对的案例

思考

功能强大的UiAutomator2能不能捕获到Toast的内容呢?

是否可以在我们的自动化脚本中主动弹出一个Toast呢?

经过我的研究发现,以上问题的答案都是肯定的,下面简要做个总结。

抓取Toast

对于Toast内容的抓取,一开始我的思路是构建一个辅助Apk,让其实现AccessibilityService的服务,在后台不停地监听并记录Toast的内容,然后通过广播、Socket、AIDL等方式将抓取到的Toast内容传入我们的UiAutomator2的脚本中,以便验证其内容的正确性。

然而实验发现,单独的一个辅助Apk完全可以抓取到Toast的内容,但是一旦我们的UiAutomator2脚本启动后,辅助Apk的服务就挂了,看似UiAutomator2非常霸道地独占了整个AccessibilityService,毕竟其底层原理就是基于后者的,因此该思路不可行。

不过细想,既然UiAutomator2的底层就是基于AccessibilityService的,那么我们完全可以直接使用UiAutomator2中的AccessibilityService接口实现监听Toast功能。

以下为实现方法:

利用UiAutomation的接口注册AccessibilityEvent的监听器

private static void initToastListener() {

mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(new UiAutomation.OnAccessibilityEventListener() {

@Override

public void onAccessibilityEvent(AccessibilityEvent event) {

Log.i(TAG, "onAccessibilityEvent: " + event.toString());

//判断是否是通知事件

if (event.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {

return;

}

//获取消息来源

String sourcePackageName = (String) event.getPackageName();

//获取事件具体信息

Parcelable parcelable = event.getParcelableData();

//如果不是下拉通知栏消息,则为其它通知信息,包括Toast

if (!(parcelable instanceof Notification)) {

toastMessage = (String) event.getText().get(0);

toastOccurTime = event.getEventTime();

Log.i(TAG, "Latest Toast Message: " + toastMessage + " [Time: " + toastOccurTime + ", Source: " + sourcePackageName + "]");

}

}

});

}

在beforeClass方法中初始化该监听器

@BeforeClass

public static void beforeClass() {

...

//初始化ToastListener

initToastListener();

}

在测试用例的方法中验证抓取到的Toast内容是否正确

@Test

public void testCatchToast() {

mCommonUtil.sleep(2000);

final long startTimeMillis = SystemClock.uptimeMillis();

mDevice.wait(Until.findObject(By.res("com.crazy.demo:id/button")), TIMEOUT).click();

boolean isSuccessfulCatchToast;

while (true) {

long currentTimeMillis = SystemClock.uptimeMillis();

long elapsedTimeMillis = currentTimeMillis - startTimeMillis;

if (elapsedTimeMillis > 5000L) {

Log.i(TAG, "超过5s未能捕获到预期Toast!");

isSuccessfulCatchToast = false;

break;

}

if (toastOccurTime > startTimeMillis) {

isSuccessfulCatchToast = "测试抓取Toast".equals(toastMessage);

break;

}

}

Assert.assertTrue("捕获预期Toast失败!", isSuccessfulCatchToast);

}

测试结果如下:

抓取Toast.jpg

抓取Toast的日志.png

弹出Toast

这个需求其实是出于好奇去研究的,我会想能不能在我们的UiAutomator2的脚本中直接弹出Toast信息呢?例如我们的每条案例执行结束了,可以弹出一个Toast提示一下测试人员。

如果我们另外再写一个辅助Apk肯定是可以完成这个需求的,但是我这里说的是直接在我们的脚本中弹出Toast,很明显是可以的。

实现如下:

我封装了一个ToastUtil的工具类,主要代码片段如下:

public class ToastUtil {

...

static {

Thread workerThread = new Thread() {

@Override

public void run() {

super.run();

//这里是关键,要在线程中弹出Toast,必须加入Looper

Looper.prepare();

mWorkerHandler = new Handler();

Looper.loop();

}

};

workerThread.start();

}

...

/**

* 显示Toast公开方法

*/

public void showToast(String msg) {

ShowToastRunnable runnable = new ShowToastRunnable(msg);

if (mWorkerHandler != null) {

mWorkerHandler.post(runnable);

}

}

private class ShowToastRunnable implements Runnable {

private String msg;

ShowToastRunnable(String msg) {

this.msg = msg;

}

@Override

public void run() {

Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();

}

}

}

在测试用例的方法中使用上述工具类

@Test

public void testShowToast() {

mCommonUtil.sleep(2000);

mDevice.wait(Until.findObject(By.desc("更多选项")), TIMEOUT).click();

mCommonUtil.sleep(1000);

mDevice.pressBack();

mToastUtil.showToast("测试结束");

//最好在这里加下延时,不然子线程还没来得及显示Toast,主线程可能就结束了

mCommonUtil.sleep(1000);

}

/**

* 更简便的方式

* @param str

*/

void showToast(final String str){

new Handler(mTargetContext.getMainLooper()){

public void handleMessage (Message msg) {

Toast.makeText(mTargetContext, str, Toast.LENGTH_LONG).show();

}

}.sendEmptyMessage(0);

}

测试结果如下:

弹出Toast.jpg

最后,UiAutomator2更多强大的功能有待我们继续挖掘!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值