Appium 修改 appium-uiautomator2-server 源码解决 Android UI 自动化不确定弹框问题

梧桐 · 2023年11月28日 · 最后由 梧桐 回复于 2023年11月29日 · 4301 次阅读

1.背景

由于测试应用不确定弹框较多,如果在脚本中判断很耗时间,因此修改 appium-uiautomator2-server 源码添加监听解决这个问题,目前实现了可配置式的监听,暂时只支持 text、resourceId、textContains、content-desc4 中查找方式,且只支持简单的点击操作,具体可以根据自己的需求进行扩展。

2.下载 appium-uiautomator2-server

根据你所使用的 apppium 版本下载对应的 uiautomator2server 源码

3.使用 AndroidStudio 打开项目

4.添加对弹框的监听

在 utils 包下新建 WatcherUtils 类,用于读取配置文档中需要监听的内容

 WatcherUtils 代码如下:

package io.appium.uiautomator2.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * 监听工具类,用于读取配置文件中需要监听的内容
 */
public class WatcherUtils {

    /**
     * 读取配置文件
     * @return
     */
    public static List<String> readWatchers(){
        List<String> watcherList = new ArrayList<>();
        String encoding = "GBK";
        File file = new File("/data/local/tmp/watchers.txt");
        if (file.isFile() && file.exists()){
            try {
                InputStream is = new FileInputStream(file);
                InputStreamReader isr = new InputStreamReader(is, encoding);
                BufferedReader br = new BufferedReader(isr);
                String line = "";
                while ((line = br.readLine()) != null){
                    watcherList.add(line);
                }
                br.close();
                isr.close();
                is.close();
            }catch (Exception ex){
                Logger.error("read watchers fail");
            }
        }
        return watcherList;
    }
}

 在 AndroidServer 类中调用获取配置文件,并添加监听

 代码如下:

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.appium.uiautomator2.server;

import static io.appium.uiautomator2.utils.Device.getUiDevice;

import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiWatcher;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import io.appium.uiautomator2.http.HttpServer;
import io.appium.uiautomator2.utils.Logger;
import io.appium.uiautomator2.utils.WatcherUtils;

public class AndroidServer {
    private final HttpServer webServer;

    public AndroidServer(int port) {
        webServer = new HttpServer(port);
        init();
        List<String> watcherList = WatcherUtils.readWatchers();
        watcherHandler(watcherList);
        Logger.info("AndroidServer created on port " + port);
    }

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

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

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

    public int getPort() {
        return webServer.getPort();
    }

    /**
     * 注册监听
     * @param watcherList
     */
    private void watcherHandler(List<String> watcherList){
        //将配置文件中的内容添加到监听
        for (String watcher : watcherList){
            String[] str = watcher.split("#");
            getUiDevice().registerWatcher(str[0], new UiWatcher() {
                @Override
                public boolean checkForCondition() {
                    BySelector selector = findTarget(str[1], str[2]);
                    if (getUiDevice().hasObject(selector)){
                        if (getUiDevice().hasObject(findTarget(str[3], str[4]))){
                            getUiDevice().findObject(findTarget(str[3], str[4])).click();
                        }
                        return true;
                    }
                    return false;
                }
            });
        }
        //设置监听任务
        TimerTask watcherTask = new TimerTask() {
            @Override
            public void run() {
                try {
                    getUiDevice().runWatchers();
                }catch (Exception ex){}
            }
        };
        Timer timer = new Timer("WatcherTimer");
        //每隔5s监听一次
        timer.scheduleAtFixedRate(watcherTask, 1000, 5000);
    }

    private BySelector findTarget(String key, String value){
        BySelector selector = null;
        switch (key){
            case "text":
                selector = By.text(value);
                break;
            case "resourceId":
                selector = By.res(value);
                break;
            case "textContains":
                selector = By.textContains(value);
                break;
            case "content-desc":
                selector = By.desc(value);
                break;
            default:
                break;
        }
        return selector;
    }
}

重新编译,在项目根目录下打开 cmd,执行命令

gradlew.bat clean assembleServerDebug assembleServerDebugAndroidTest

编译完成后会在项目\app\build\outputs\apk 目录下生成 2 个 apk,将将 2 个 apk 替换掉 appium\node_modules\appium-uiautomator2-server\apks 目录下的 apk

5.创建配置文件

创建 watchers.txt 文件,内容格式如下:
监听名称 # 触发监听时 By# 触发监听时的值 # 触发监听后操作 By# 触发监听后操作值

 例如上面的监听即为当发现页面存在 resource-id 为 close-button-icon 时,点击 resource-id 为 close-button-icon 的目标

6.测试

将 watchers.txt push 到手机\data\local\tmp 目录下,并见手机中的 io.appium.uiautomator2.server、io.appium.uiautomator2.server.test 卸载,安装上面编译生成的 2 个 apk 后,执行命令启动 uiautomator2 服务

adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner

然后进入添加监听的页面,可以看到会自动关掉弹框

后续只需要在 watchers.txt 中添加相应的配置并 push 到手机中即可

共收到 5 条回复 时间 点赞

adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner

执行了之后会一直在后台保持?碰到就给你点掉?

是的,这个命令是手动启动测试的,实际运行 appium 时会自动启动的

其他自动化工具也可以用这个命令吧?

认真ing 回复

如果不是像 appium 基于 uiautomator2 的话,应该可以安装打包生成的 2 个 apk 后使用命令启动

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册