Appium基础学习之 | 设备端Appium Server APK源码简单分析

在《Appium基础学习之 | Appium-Desktop日志分析》中说到,推送了一个Package为io.appium.uiautomator2.server.test的APK到设备端,然后通过adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJunitRunner运行。经过前面学习UiAutomator2.0基于Instrumentation运行,从这里可以大概猜测io.appium.uiautomator2.server.test就是一个基于Instrumentation运行的Android工程,下面开始进入源码分析。

源码下载地址:https://github.com/appium/appium-uiautomator2-server,下载源码解压

在源码中找到appium-uiautomator2-server-master\app\src\androidTestServer\java\io\appium\uiautomator2\server\test目录,在目录下找到入口类AppiumUiAutomator2Server类

1.AppiumUiAutomator2Server类

@RunWith(AndroidJUnit4.class)
public class AppiumUiAutomator2Server {
    private static ServerInstrumentation serverInstrumentation;
    @Test
    public void startServer() {
        if (serverInstrumentation == null) {
            serverInstrumentation = ServerInstrumentation.getInstance();
            Logger.info("[AppiumUiAutomator2Server]", " Starting Server");
            try {
                while (!serverInstrumentation.isServerStopped()) {
                    SystemClock.sleep(1000);
                    serverInstrumentation.startServer();
                }
            } catch (SessionRemovedException e) {
                //Ignoring SessionRemovedException
            }
        }
    }
}

看到代码一目了然,基于AndroidJunit4运行;先是调用了ServerInstrumentation的getInstance方法

2.ServerInstrumentation类

在appium-uiautomator2-server-master\app\src\main\java\io\appium\uiautomator2\server目录下

public static synchronized ServerInstrumentation getInstance() {
        if (instance == null) {
            instance = new ServerInstrumentation(getApplicationContext(), getServerPort());
        }
        return instance;
    }

getInstance方法实例化一个ServerInstrumentation对象,入参是getApplicationContext()、getServerPort()执行方法返回的内容,getApplicationContext得到是一个Context上下文对象,这个是Android应用中Application中唯一的。再到getServerPort()方法返回是一个6790端口,这个端口就是由Appium Server机器的8200端口转发到设备端的6790端口接收请求。

public class ServerConfig {
    private final static int PORT = 6790;

    public static int getServerPort() {
        return PORT;
    }
}

然后回到AppiumUiAutomator2Server类,实例化ServerInstrumentation对象后,调用了它的startServer方法

private HttpdThread serverThread;

public void startServer() throws SessionRemovedException {
       ......

        serverThread = new HttpdThread(this.serverPort);
        serverThread.start();
     ......
    }

在startServer方法中实例化了HttpdThread这个内部类对象,继承Thread所以它是一个线程。

3.HttpdThread类

是ServerInstrumentation的内部类

private class HttpdThread extends Thread {

        private final AndroidServer server;
        private Looper looper;

        public HttpdThread(int serverPort) {
            // Create the io.appium.uiautomator2.server but absolutely do not start it here
            server = new AndroidServer(serverPort);
        }

       @Override
        public void run() {
            Looper.prepare();
            looper = Looper.myLooper();
            startServer();
            Looper.loop();
        }

}

HttpdThread构造函数实例化了AndroidServer对象,

4.AndroidServer类

在appium-uiautomator2-server-master\app\src\main\java\io\appium\uiautomator2\server目录下

public class AndroidServer {
    private final HttpServer webServer;

    public AndroidServer(int port) {
        webServer = new HttpServer(port);
        init();
        Logger.info("AndroidServer created on port " + port);
    }

    protected void init() {
        webServer.addHandler(new AppiumServlet());
    }

   public void start() {
        webServer.start();
    }

}

实例化了一个HttpServer对象,然后执行addHandler方法入参一个AppiumServlet对象

5.HttpServer类

在appium-uiautomator2-server-master\app\src\main\java\io\appium\uiautomator2\http目录下

public class HttpServer {
    private final int port;
    private final List handlers = new ArrayList();
    private Thread serverThread;

    public HttpServer(int port) {
        this.port = port;
    }

    public void addHandler(IHttpServlet handler) {
        handlers.add(handler);
    }

 

}

到HttpServer类中,看到addHander做的就是把AppiumServlet对象加入到了list列表中

6.AppiumServlet类

在appium-uiautomator2-server-master\app\src\main\java\io\appium\uiautomator2\server目录下

进入到AppiumServlet类中,可以看到它把appium操作对象都加入到了一个Map中,包括get、post、delete三种请求的所有操作

(1)Delete

 protected static ConcurrentMap deleteHandler = new ConcurrentHashMap<>();

 private void registerDeleteHandler() {
        register(deleteHandler, new DeleteSession("/wd/hub/session/:sessionId"));
    }

(2)get

protected static ConcurrentMap getHandler = new ConcurrentHashMap<>();

 private void registerGetHandler() {
        register(getHandler, new Status("/wd/hub/status"));
        register(getHandler, new GetSessionDetails("/wd/hub/session/:sessionId"));
        register(getHandler, new CaptureScreenshot("/wd/hub/session/:sessionId/screenshot"));
        register(getHandler, new GetScreenOrientation("/wd/hub/session/:sessionId/orientation"));
        register(getHandler, new GetRotation("/wd/hub/session/:sessionId/rotation"));
        register(getHandler, new GetText("/wd/hub/session/:sessionId/element/:id/text"));
        register(getHandler, new GetElementAttribute("/wd/hub/session/:sessionId/element/:id/attribute/:name"));
        register(getHandler, new GetRect("/wd/hub/session/:sessionId/element/:id/rect"));
        register(getHandler, new GetSize("/wd/hub/session/:sessionId/element/:id/size"));
        register(getHandler, new GetName("/wd/hub/session/:sessionId/element/:id/name"));
        // W3C endpoint
        register(getHandler, new GetElementScreenshot("/wd/hub/session/:sessionId/element/:id/screenshot"));
        // JSONWP endpoint
        register(getHandler, new GetElementScreenshot("/wd/hub/session/:sessionId/screenshot/:id"));
        register(getHandler, new Location("/wd/hub/session/:sessionId/element/:id/location"));
        register(getHandler, new GetDeviceSize("/wd/hub/session/:sessionId/window/:windowHandle/size"));
        register(getHandler, new Source("/wd/hub/session/:sessionId/source"));
        register(getHandler, new GetSystemBars("/wd/hub/session/:sessionId/appium/device/system_bars"));
        register(getHandler, new GetBatteryInfo("/wd/hub/session/:sessionId/appium/device/battery_info"));
        register(getHandler, new GetSettings("/wd/hub/session/:sessionId/appium/settings"));
        register(getHandler, new GetDevicePixelRatio("/wd/hub/session/:sessionId/appium/device/pixel_ratio"));
        register(getHandler, new FirstVisibleView("/wd/hub/session/:sessionId/appium/element/:id/first_visible"));
        register(getHandler, new GetAlertText("/wd/hub/session/:sessionId/alert/text"));
        register(getHandler, new GetDeviceInfo("/wd/hub/session/:sessionId/appium/device/info"));
    }

(3)post

protected static ConcurrentMap postHandler = new ConcurrentHashMap<>();

private void registerPostHandler() {
        register(postHandler, new NewSession("/wd/hub/session"));
        register(postHandler, new FindElement("/wd/hub/session/:sessionId/element"));
        register(postHandler, new FindElements("/wd/hub/session/:sessionId/elements"));
        register(postHandler, new Click("/wd/hub/session/:sessionId/element/:id/click"));
        register(postHandler, new Click("/wd/hub/session/:sessionId/appium/tap"));
        register(postHandler, new Clear("/wd/hub/session/:sessionId/element/:id/clear"));
        register(postHandler, new RotateScreen("/wd/hub/session/:sessionId/orientation"));
        register(postHandler, new RotateScreen("/wd/hub/session/:sessionId/rotation"));
        register(postHandler, new PressBack("/wd/hub/session/:sessionId/back"));
        register(postHandler, new SendKeysToElement("/wd/hub/session/:sessionId/element/:id/value"));
        register(postHandler, new SendKeysToElement("/wd/hub/session/:sessionId/keys"));
        register(postHandler, new Swipe("/wd/hub/session/:sessionId/touch/perform"));
        register(postHandler, new TouchLongClick("/wd/hub/session/:sessionId/touch/longclick"));
        register(postHandler, new OpenNotification("/wd/hub/session/:sessionId/appium/device/open_notifications"));
        register(postHandler, new PressKeyCode("/wd/hub/session/:sessionId/appium/device/press_keycode"));
        register(postHandler, new LongPressKeyCode("/wd/hub/session/:sessionId/appium/device/long_press_keycode"));
        register(postHandler, new Drag("/wd/hub/session/:sessionId/touch/drag"));
        register(postHandler, new Flick("/wd/hub/session/:sessionId/touch/flick"));
        register(postHandler, new ScrollTo("/wd/hub/session/:sessionId/touch/scroll"));
        register(postHandler, new MultiPointerGesture("/wd/hub/session/:sessionId/touch/multi/perform"));
        register(postHandler, new W3CActions("/wd/hub/session/:sessionId/actions"));
        register(postHandler, new TouchDown("/wd/hub/session/:sessionId/touch/down"));
        register(postHandler, new TouchUp("/wd/hub/session/:sessionId/touch/up"));
        register(postHandler, new TouchMove("/wd/hub/session/:sessionId/touch/move"));
        register(postHandler, new UpdateSettings("/wd/hub/session/:sessionId/appium/settings"));
        register(postHandler, new NetworkConnection("/wd/hub/session/:sessionId/network_connection"));
        register(postHandler, new ScrollToElement("/wd/hub/session/:sessionId/appium/element/:id/scroll_to/:elementId"));
        register(postHandler, new GetClipboard("/wd/hub/session/:sessionId/appium/device/get_clipboard"));
        register(postHandler, new SetClipboard("/wd/hub/session/:sessionId/appium/device/set_clipboard"));
        register(postHandler, new AcceptAlert("/wd/hub/session/:sessionId/alert/accept"));
        register(postHandler, new DismissAlert("/wd/hub/session/:sessionId/alert/dismiss"));
    }

7.HttpServer类

在appium-uiautomator2-server-master\app\src\main\java\io\appium\uiautomator2\http目录下

经过一串系列的实例化对象干完该干的数据初始化后,接着回到HttpdThread,是继承Thread线程,所以会执行run方法,run方法这里又使用到了Looper,真是无处不在,然后调用startServer方法,这个方法就不贴代码了,一直往下走调用了AndroidServer的start方法,继续调用的是HttpServer的start方法,

public void run() {

              //管理进来请求

                EventLoopGroup bossGroup = new NioEventLoopGroup(1);

                EventLoopGroup workerGroup = new NioEventLoopGroup();

                try {

                    //新建服务

                    ServerBootstrap bootstrap = new ServerBootstrap();

                   //最大请求队列  

                    bootstrap.option(ChannelOption.SO_BACKLOG, 1024);

                    //支持端口共用

                    bootstrap.option(ChannelOption.SO_REUSEADDR, true);

                    //接受请求后交给ServerInitializer处理

                    bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ServerInitializer(handlers));        

                    //绑定端口,传入的是6790,开始接收请求处理,一个channel表示一个请求

                    Channel ch = bootstrap.bind(port).sync().channel();

                   //等待请求关闭

                    ch.closeFuture().sync();

                } catch (InterruptedException ignored) {

                } finally {

                    bossGroup.shutdownGracefully();

                    workerGroup.shutdownGracefully();

                }

start方法是一个netty框架的使用,这个框架有需要了解后面再写一篇文章说这个框架吧。大概的意思就是初始化一个服务,然后接受请求,交给ServerInitializer对象来处理。接下来就看ServerInitializer的代码。

8.ServerInitializer类

在appium-uiautomator2-server-master\app\src\main\java\io\appium\uiautomator2\http目录下

public class ServerInitializer extends ChannelInitializer {

    private final List handlers;

    public ServerInitializer(List handlers) {
        this.handlers = handlers;
    }

    @Override
    public void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast("codec", new HttpServerCodec());
        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
        pipeline.addLast("io/appium/uiautomator2/handler", new io.appium.uiautomator2.http.ServerHandler(handlers));
    }
}

ServerInitializer继承ChannelInitializer作为实际处理数据类,重写initChannel方法用初始化ChannelHandler,而ChannelHandler可以理解为就是处理Channel请求的。创建ChannelPipeline对象,这个对象是每一个请求Channel必须且只有一个,它是用来管理和处理ChannelHandler的。addLast方法就是把一个ChannelHandler交给ChannelPipeline管理。前面2个先不管,直接看

pipeline.addLast("io/appium/uiautomator2/handler", new io.appium.uiautomator2.http.ServerHandler(handlers));

ChannelHandler的名字是io/appium/uiautomator2/handler,ChannelHandler的处理由ServerHandler完成

9.ServerHandler类

在appium-uiautomator2-server-master\app\src\main\java\io\appium\uiautomator2\http目录下

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
       ......

        io.appium.uiautomator2.http.IHttpRequest httpRequest = new NettyHttpRequest(request);
        io.appium.uiautomator2.http.IHttpResponse httpResponse = new NettyHttpResponse(response);

        for (io.appium.uiautomator2.http.IHttpServlet handler : httpHandlers) {
            handler.handleHttpRequest(httpRequest, httpResponse);
            if (httpResponse.isClosed()) {
                break;
            }
        }

    ......
    }

ServerHandler处理进来的请求,channelRead方法在收到请求数据触发,通过NettyHttpRequest、NettyHttpResponse对象处理请求、响应的数据;然后进入for循环。

(1)先搞清楚for循环的httpHnadlers里面的数据,它是在AndroidServer的start方法中,调用init方法把AppiumServlet对象加进入的(在上面说到delete、get、post方法的所有请求uri哪里提到的)而AppiumServlet是实现了IhttpServlrt接口的。

(2)循环取出一个AppiumServlet对象,执行handleHttpRequest方法,回到AppiumServlet类

10.AppiumServlet类

在appium-uiautomator2-server-master\app\src\main\java\io\appium\uiautomator2\server目录下

先看看handleHttpRequest方法,分别是根据发送的请求,get、post、delete进入不同的语句块

@Override
    public void handleHttpRequest(IHttpRequest request, IHttpResponse response) {
        BaseRequestHandler handler = null;
        if ("GET".equals(request.method())) {
            handler = findMatcher(request, getHandler);
        } else if ("POST".equals(request.method())) {
            handler = findMatcher(request, postHandler);
        } else if ("DELETE".equals(request.method())) {
            handler = findMatcher(request, deleteHandler);
        }
        handleRequest(request, response, handler);
    }

然后调用了findMatcher方法

protected BaseRequestHandler findMatcher(IHttpRequest request, Map handler) {
        String[] urlToMatchSections = getRequestUrlSections(request.uri());
        for (Map.Entry entry : handler.entrySet()) {
            String[] mapperUrlSections = getMapperUrlSectionsCached(entry.getKey());
            if (isFor(mapperUrlSections, urlToMatchSections)) {
                return entry.getValue();
            }
        }
        return null;
    }

经过getRequestUrlSections、getMapperUrlSectionsCached、isFor一系列方法处理后,拿到请求的uri对比后找到了执行的Handler,然后执行了handleRequest方法,先经过addHandlerAttributesToRequest方法对请求数据的处理,然后调用了handle的handle方法(如new Status(),就调用了Status的handle的方法),最终执行了handleResponse返回结果。

你可能感兴趣的:(Appium,Appium学习)