UiAutomator1.0基于AccessibilityService是否正确?这篇文章揭开
从《Appium基础学习之 | UiAutomator启动命令runtest浅析一》中最后可以看到是执行了Launcher,回到UiAutomator1.0的源码,继续看代码了;源码在\cmds\uiautomator\src\com\android\commands\uiautomator目录下
1.Launcher
public static void main(String[] args) {
// show a meaningful process name in `ps`
Process.setArgV0("uiautomator");
if (args.length >= 1) {
Command command = findCommand(args[0]);
if (command != null) {
String[] args2 = {};
if (args.length > 1) {
// consume the first arg
args2 = Arrays.copyOfRange(args, 1, args.length);
}
command.run(args2);
return;
}
}
HELP_COMMAND.run(args);
}
主函数中args这个值就是exec app_process ${base}/bin com.android.commands.uiautomator.Launcher ${args}
执行时传入的${args},通过《Appium基础学习之 | UiAutomator启动命令runtest浅析一》最后转换的值为:
exec app_process /system/bin com.android.commands.uiautomator.Launcher runtest -c io.appium.android.bootstrap.Bootstrap -e pkg com.main -e disableAndroidWatchers false -e jars AppiumBootstrap.jar
再结合Launcher的主函数来看,首先取出第一个值,也就是runtest,调用findCommand()执行
private static Command findCommand(String name) {
for (Command command : COMMANDS) {
if (command.name().equals(name)) {
return command;
}
}
return null;
}......
private static Command[] COMMANDS = new Command[] {
HELP_COMMAND,
new RunTestCommand(),
new DumpCommand(),
new EventsCommand(),
};
从代码来看,先执行了COMMANDS,从里面可以很明显的猜到是对应UiAutomator的4个方法,其他三个先不管,主要看new RunTestCommand(),来到RunTestCommand类做了什么?
2.RunTestCommand
public class RunTestCommand extends Command {
public RunTestCommand() {
super("runtest");
}@Override
public void run(String[] args) {
......
getRunner().run(mTestClasses, mParams, mDebug, mMonkey);
}}
可以看到RunTestCommand继承了Command,然后看到构造函数传入了runtest字符串,然后来看看Command类
3.Command
public class Launcher {
/**
* A simple abstraction class for supporting generic sub commands
*/
public static abstract class Command {
private String mName;public Command(String name) {
mName = name;
}/**
* Returns the name of the sub command
* @return
*/
public String name() {
return mName;
}......
public abstract void run(String args[]);
}}
Command实际是Launcher类中的一个内部静态抽象类,注意它还有一个抽象方法run,所以结合上面Launcher的主函数以及findCommand方法来看,实际上是执行了RunTestCommand中的run方法。
4.RunTestCommand
protected UiAutomatorTestRunner getRunner() {
......
}
回到上面第2点中介绍的RunTestCommand,它的run方法里面实际调用了getRunner方法。上面代码是getRunner方法,代码就省略了,可以看到返回的是UiAutomatorTestRunner对象,然后执行了run方法,所以来到了UiAutomatorTestRunner类
5.UiAutomatorTestRunner
public class UiAutomatorTestRunner {
private UiDevice mUiDevice;
public void run(List<String> testClasses, Bundle params, boolean debug, boolean monkey) {
......
start();
System.exit(EXIT_OK);
}
protected void start() {
......
UiAutomationShellWrapper automationWrapper = new UiAutomationShellWrapper();
automationWrapper.connect();mUiDevice = UiDevice.getInstance();
mUiDevice.initialize(new ShellUiAutomatorBridge(automationWrapper.getUiAutomation()));
}}
来到UiAutomatorTestRunner的run方法,然后调用了start方法;start方法实例化了UiAutomationShellWrapper对象调用connect方法。这里还有另外一个非常重要的就是mUiDevice,这个到最后面说。
6.UiAutomationShellWrapper
public class UiAutomationShellWrapper {
private UiAutomation mUiAutomation;
public void connect() {
if (mHandlerThread.isAlive()) {
throw new IllegalStateException("Already connected!");
}
mHandlerThread.start();
mUiAutomation = new UiAutomation(mHandlerThread.getLooper(),
new UiAutomationConnection());
mUiAutomation.connect();
}public UiAutomation getUiAutomation() {
return mUiAutomation;
}}
实例化了一个UiAutomation对象,入参是一个Looper对象,UiAutomationConnection对象,然后调用了UiAutomation的connect方法。UiAutomation是android的类了,所以这时候来到Android的源码中找。
7.UiAutomation
public final class UiAutomation {
private final IUiAutomationConnection mUiAutomationConnection;
public UiAutomation(Looper looper, IUiAutomationConnection connection) {
if (looper == null) {
throw new IllegalArgumentException("Looper cannot be null!");
}
if (connection == null) {
throw new IllegalArgumentException("Connection cannot be null!");
}
mUiAutomationConnection = connection;
mClient = new IAccessibilityServiceClientImpl(looper);
}
public void connect() {
......
mUiAutomationConnection.connect(mClient);
......
}
}}
UiAutomationShellWrapper实例化UiAutomation中入参的两个对象,Looper对象,UiAutomationConnection对象,但是来到UiAutomation构造函数,变成了IUiAutomationConnection对象,然后调用了connect方法再次调用了IUiAutomationConnection对象的connect方法。
8.UiAutomationConnection
public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
public void connect(IAccessibilityServiceClient client) {
......
registerUiTestAutomationServiceLocked(client);
}
}private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client) {
IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
| AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS;
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS);
try {
// Calling out with a lock held is fine since if the system
// process is gone the client calling in will be killed.
manager.registerUiTestAutomationService(mToken, client, info);
mClient = client;
} catch (RemoteException re) {
throw new IllegalStateException("Error while registering UiTestAutomationService.", re);
}
}}
在4.3的android源码包中没有找到IUiAutomationConnection的实现类,找到了UiAutomationConnection类,它是继承IUiAutomationConnection,所以说上面的对象转换是没有任何问题的。connect方法调用了registerUiTestAutomationServiceLocked方法,这个registerUiTestAutomationServiceLocked方法就是最后我们找到UiAutomator与AccessibilityService连接的依据。方法中实例化AccessibilityServiceInfo对象,然后设置了这个对象的成员属性值,eventTypes、feedbackType、flags等等信息,然后通过IAccessibilityManager对象注册AccessibilityService,成功连接上AccessibilityService服务。
然后回到第5点的中提到的mUiDevice,再结合第6点中getUiAutomation方法,就可以知道,mUiDevice是通过uiautomation对象连接上AccessibilityService的对象,所以通过mUiDevice后面的操作都是通过注入AccessibilityService的APIs去完成操作的。
总结:
UiAutomator1.0基于AccessibilityService是否正确?可以理解是,但我觉得应该更明确点,UiAutomator1.0是基于AccessibilityService APIs完成在应用中的操作,对于AccessibilityService来说就是接收事件完成事件的操作。