Android 自动化交互可以代替人工完成重复性的工作,包括通过自动操作 App 进行黑盒测试和第三方 App 的自动运行。常见的自动化交互包含启动 App、view 的点击、拖拽和文本输入等。随着 App 安防能力的提升,要想实现完整流程的自动化交互变的越来越困难,本文主要探讨目前常见的自动化交互方案以及不同方案的优劣和应用场景。
1 传统执行脚本方案
ADB 是 Google 提供的能够和 Android 设备进行交互的命令行工具,我们可以编写脚本按照事先设计好的顺序,一个一个执行各个事件。ADB 执行操作需要事先获取界面元素的坐标(获取坐标方法可以利用 uiautomator 或者 dump xml 的方法,这里不是讨论的重点),然后把坐标传入作为命令行参数。
adb shell input tap 100 500
复制代码
上面命令是模拟点击屏幕坐标为(100, 500)处的控件。
adb shell input swipe 100 500 200 600
复制代码
上面命令是模拟手指在屏幕上向右下方滑动的一个操作。
adb shell input keyevent "KEYCODE_BACK"
复制代码
上面命令模拟返回按键的点击。
一次完整的自动化交互流程可由上面一系列命令顺序执行。
ADB 脚本方式的优点
实现简单,只需要获取目标元素的坐标等简单信息即可完成相关操作
可以实现对 webview 的自动化交互
ADB 脚本方式的缺点
灵活度不够,依赖于写死的坐标,App 界面变更引起的 view 位置变换会让脚本中相关命令无法执行,需要重新分析页面坐标
需要建立 ADB 链接或套接字链接,交互过程中网络状况的变化会影响自动化交互效果
ADB 脚本方式应用场景
交互简单、迭代频率低,安防级别比较低的 App
webview 页面,flutter 开发的 App
2 Android 原生方法实现自动化交互
我们可以借助各种插件化框架来控制 App 页面的界面元素,其中一种思路就是在插件中借助 ActivityLifecycleCallbacks 来监听各个 activity 的生命周期。
public class MyApplication extends Application {
private static final String TAG = "MyApplication";
//声明一个监听Activity们生命周期的接口
private ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacks() {
/**
* application下的每个Activity声明周期改变时,都会触发以下的函数。
*/
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//如何区别参数中activity代表你写的哪个activity。
if (activity.getClass() == MainActivity.class)
Log.d(TAG, "MainActivityCreated.");
else if(activity.getClass()== SecondActivity.class)
Log.d(TAG, "SecondActivityCreated.");
}
@Override
public void onActivityStarted(Activity activity) {
Log.d(TAG, "onActivityStarted.");
}
@Override
public void onActivityResumed(Activity activity) {
Log.d(TAG, "onActivityResumed.");
}
@Override
public void onActivityPaused(Activity activity) {
Log.d(TAG, "onActivityPaused.");
}
@Override
public void onActivityStopped(Activity activity) {
Log.d(TAG, "onActivityStopped.");
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.d(TAG, "onActivityDestroyed.");
}
};
@Override
public void onCreate() {
super.onCreate();
//注册自己的Activity的生命周期回调接口。![Alt text](./WechatIMG59.png)
registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
}
@Override
public void onTerminate() {
//注销这个接口。
unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks);
super.onTerminate();
}
}
复制代码
监听到 activity 的活动后,可以借助 uiautomator 分析 activity 界面元素的 viewId 以及属性,不同情况的界面 view 可以采用不同的自动化方法。
2.1 简单 view 的处理方式
如下图:
像这类 view,可以直接获取到 resource id ,并且确认可点击属性为 true,操作方式比较简单, 可以在监听到的 activity 生命周期中执行如下操作:
int fl_btn = activity.getResources().getIdentifier("dashboard_title", "id", "com.android.settings");
View v = activity.findViewById(fl_btn);
v.performClick();
复制代码
2.2 隐藏属性的 view 的处理方式
在一些对 view 的属性进行隐藏,特别是利用 React Native 等混合开发的页面,上面的方法不再生效,如下图所示的 view:
如图,选中的 viewgroup 及其子 view 的 clickable 属性均为 false,并且无法获取到 view 的 resource id,这时候可以利用图中 dump 出的布局信息,借助 Xpath 元素定位工具来获取到界面的 view,由于这些 view 的点击属性为 false,因此通过调用 performClick 来实现点击的方法已经无效,此时考虑在 click 更底层的与触摸事件传递相关的比较重要的类:MotionEvent, MotionEvent 可以仿真几乎所有的交互事件,包括点击,滑动,双指操作等。以单击为例:
private void simulateClick(View view, float x, float y) {
long time = SystemClock.uptimeMillis();//必须是 SystemClock.uptimeMillis()。
MotionEvent downEvent = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, x, y, 0);
time += 500;
MotionEvent upEvent = MotionEvent.obtain(time, time, MotionEvent.ACTION_UP, x, y, 0);
view.onTouchEvent(downEvent);
view.onTouchEvent(upEvent);
}
复制代码
如果是滑动操作,可以在起始位置中间添加若干 ACTION_MOVE 类型的 MotionEvent. 综上所述,借助系统原生方法时间交互自动化的优缺点大致如下:
借助插件框架实现自动化交互的优点
可维护性强,因为可以直接获取到界面的 view 对象,因此即使页面布局发生变化,只要 view 还存在,就不需要对代码进行修改
仿真效果更好,比脚本方式更接近真人操作
借助插件框架实现自动化交互的不足
对 webview、flutter 框架 App 支持不好
应用场景
版本迭代频繁的 App
非 flutter 框架开发的 App
上面分析了两种常用的模拟真实用户进行自动化操作的方法,脚本方式和调用原生方法的方式,这两种方法基本上可以完成简单的交互流程,在此基础上,我们还可以去深究一些更深层次的交互实现,比如自动化过各种验证等,也可以基于这两种方法来完成。
作者介绍:
李涛,2015 年 3 月加入去哪儿网,客户端开发工程师,曾经担任酒店业务和自助入离机项目开发。现负责国际酒店抓取系统开发与维护。
本文转载自公众号 Qunar 技术沙龙(ID:QunarTL)。
原文链接:
Android 自动化交互实践
评论