Android 自动化交互可以代替人工完成重复性的工作,包括通过自动操作 App 进行黑盒测试和第三方 App 的自动运行。常见的自动化交互包含启动 App、view 的点击、拖拽和文本输入等。随着 App 安防能力的提升,要想实现完整流程的自动化交互变的越来越困难,本文主要探讨目前常见的自动化交互方案以及不同方案的优劣和应用场景。
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 脚本方式的优点
ADB 脚本方式的缺点
ADB 脚本方式应用场景
我们可以借助各种插件化框架来控制 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的生命周期回调接口。
registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
}
@Override
public void onTerminate() {
//注销这个接口。
unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks);
super.onTerminate();
}
}
监听到 activity 的活动后,可以借助 uiautomator 分析 activity 界面元素的 viewId 以及属性,不同情况的界面 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();
在一些对 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. 综上所述,借助系统原生方法时间交互自动化的优缺点大致如下:
借助插件框架实现自动化交互的优点
借助插件框架实现自动化交互的不足
应用场景
上面分析了两种常用的模拟真实用户进行自动化操作的方法,脚本方式和调用原生方法的方式,这两种方法基本上可以完成简单的交互流程,在此基础上,我们还可以去深究一些更深层次的交互实现,比如自动化过各种验证等,也可以基于这两种方法来完成。
作者介绍:
李涛,2015 年 3 月加入去哪儿网,客户端开发工程师,曾经担任酒店业务和自助入离机项目开发。现负责国际酒店抓取系统开发与维护。
本文转载自公众号 Qunar 技术沙龙(ID:QunarTL)。
原文链接: