Appium基础学习之 | UiAutomator启动命令runtest浅析二

    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来说就是接收事件完成事件的操作。

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值