一、功能概述

  1. 属于Android UI 自动化测试的核心技术之一,允许应用程序在不需要人工干预的情况下,自动操作界面元素进行测试。
  2. 实现思路:执行指定命令,系统会将当前界面的 UI 控件信息(包括控件类型、文本内容、坐标位置等)以 XML 格式保存到指定文件中。之后,程序会解析这个 XML 文件,找到特定的控件(如按钮、文本框、输入框等),获取它们的坐标位置,进而实现自动点击、输入等操作。
  3. 涉及命令:/system/bin/uiautomator dump --compressed  [文件路径] 。作用是使用 Android 系统内置的 uiautomator 工具来获取当前屏幕界面的布局信息,并以压缩格式保存为 XML 文件。

二、实现流程

1、核心工具类

1.1、UiAutomatorUtil 

import android.text.TextUtils;
import android.util.Log;
import 你的包名.util.ShellUtil;
import java.io.File;

/**
 * Description:调用系统内置的UiAutomator工具操作类
 */
public class UiAutomatorUtil {

    /** 根据文本内容点击控件 */
    public static boolean clickByText(String classTag, String text, String uiXmlPath) throws Exception {
        // 1、调用系统内置的uiautomator工具,以压缩格式输出XML(减少文件体积),保存结果到指定路径的文件
        String execCmdResult = executeUiAutomatorDump(uiXmlPath);
        if (TextUtils.isEmpty(execCmdResult)) {
            Log.e(classTag + "_UiAutomatorUtil", "clickByText(),执行系统内置的uiautomator工具时发生异常");
            return false;
        }
        Thread.sleep(1000); // 确保文件写入完成

        // 2、解析Xml文本内容以获取坐标
        UiXmlParser.Rect bounds = UiXmlParser.findBoundsByText(new File(uiXmlPath), text);
        if (bounds == null) {
            Log.e(classTag + "_UiAutomatorUtil", "clickByText(),未找到文本控件: " + text);
            return false;
        } else {
            Log.i(classTag + "_UiAutomatorUtil", "clickByText(),找到文本控件,文本内容是: " + text);
        }

        // 3、计算中心点并点击
        UiXmlParser.Point center = bounds.getCenter();
        ShellUtil.getExecCmdResult("input tap " + center.x + " " + center.y);
        Log.i(classTag + "_UiAutomatorUtil", "clickByText(),点击的文本内容:" + text + ",坐标: (" + center.x + "," + center.y + ")");
        return true;
    }

    /**
     * 执行 uiautomator dump 命令,增加重试机制以提高成功率
     * @param uiXmlPath XML文件保存路径
     * @return 执行结果
     */
    private static String executeUiAutomatorDump(String uiXmlPath) throws Exception {
        String command = "/system/bin/uiautomator dump --compressed " + uiXmlPath;
        String result = null;
        int maxRetries = 3;

        for (int i = 0; i < maxRetries; i++) {
            try {
                result = ShellUtil.getExecCmdResult(command);
                // 检查是否包含错误信息
                if (result != null && !result.contains("ERROR: could not get idle state")) {
                    return result;
                }
            } catch (Exception e) {
                Log.e("UiAutomatorUtil", "executeUiAutomatorDump() 第" + (i+1) + "次尝试失败: " + e.getMessage());
            }

            // 等待一段时间再重试
            if (i < maxRetries - 1) {
                Thread.sleep(2000);
            }
        }

        // 如果所有重试都失败,返回最后一次的结果
        return result;
    }


    /** 在顶部输入框输入文本 */
    private static boolean inputText(String text) throws Exception {
        try {
            // 1、调用系统内置的uiautomator工具,以压缩格式输出XML(减少文件体积),保存结果到指定路径的文件
            ShellUtil.getExecCmdResult("/system/bin/uiautomator dump --compressed  " + fullSensorTestUiXmlPath);
            Thread.sleep(1000); // 确保文件写入完成

            // 2、解析Xml文本内容以获取输入框坐标(输入框的提示文本(尾部带空格)为 "Only input integer number ")  TODO 此处可修改为你的指定文本
            UiXmlParser.Rect bounds = UiXmlParser.findBoundsByText(new File(fullSensorTestUiXmlPath), "Only input integer number ");
            if (bounds == null) {
                Log.e(TAG, "inputText(),未找到输入框控件");

                // 按照输入框的文本单位值是"Hz"进行查找
                bounds = UiXmlParser.findBoundsByText(new File(fullSensorTestUiXmlPath), "Hz");
                if (bounds == null) {
                    Log.e(TAG, "inputText(),仍然未找到输入框控件");
                    return false;
                }
            } else {
                Log.i(TAG, "inputText(),已找到输入框控件");
            }

            // 3、计算中心点并点击,激活输入框
            UiXmlParser.Point center = bounds.getCenter();
            if (center == null) {
                Log.e(TAG, "inputText(),无法计算输入框中心点");
                return false;
            }

            String tapResult = ShellUtil.getExecCmdResult("input tap " + center.x + " " + center.y);
            if (tapResult == null) {
                Log.e(TAG, "inputText(),点击输入框失败");
                return false;
            }
            Thread.sleep(500); // 等待输入框激活

            // 4、输入文本
            String inputResult = ShellUtil.getExecCmdResult("input text " + text);
            if (inputResult == null) {
                Log.e(TAG, "inputText(),输入文本失败");
                return false;
            }
            Log.i(TAG, "inputText(),在输入框中输入文本: " + text);
            return true;
        } catch (Exception e) {
            Log.e(TAG, "inputText(),输入文本时发生异常: " + e.getMessage());
            return false;
        }
    }
}

1.2、UiXmlParser

import android.util.Log;
import org.w3c.dom.Document;
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.NodeList;

/**
 * Description:XML文件解析工具类
 */
public class UiXmlParser {
    private static final String TAG = UiXmlParser.class.getSimpleName();

    /** 根据文本内容查找元素 */
    public static Rect findBoundsByText(File xmlFile, String targetText) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(xmlFile);

            NodeList nodes = doc.getElementsByTagName("node");
            for (int i = 0; i < nodes.getLength(); i++) {
                org.w3c.dom.Node node = nodes.item(i);
                String text = node.getAttributes().getNamedItem("text").getNodeValue();
                if (text != null && text.equals(targetText)) {
                    String bounds = node.getAttributes().getNamedItem("bounds").getNodeValue();
                    return parseBounds(bounds);
                }
            }
            Log.i(TAG, "findBoundsByText(),根据文本内容查找元素: " + targetText);
        } catch (Exception e) {
            Log.e(TAG, "findBoundsByText(),Failed to parse xml: " + e.getMessage());
        }
        return null;
    }

    /** 解析bounds字符串 */
    private static Rect parseBounds(String boundsStr) {
        // 示例格式:[100,200][300,400]
        String[] parts = boundsStr.replace("[", "").split("]")[0].split(",");
        int left = Integer.parseInt(parts[0]);
        int top = Integer.parseInt(parts[1]);
        parts = boundsStr.split("\\[")[2].replace("]", "").split(",");
        int right = Integer.parseInt(parts[0]);
        int bottom = Integer.parseInt(parts[1]);

        Log.i(TAG, "parseBounds(),解析bounds字符串: " + boundsStr);
        return new Rect(left, top, right, bottom);
    }

    public static class Rect {
        public int left, top, right, bottom;
        public Rect(int l, int t, int r, int b) {
            left = l; top = t; right = r; bottom = b;
        }
        public Point getCenter() {
            return new Point((left + right) / 2, (top + bottom) / 2);
        }
    }

    public static class Point {
        public int x, y;
        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}

2、执行指令工具类

import android.os.Build;
import android.util.Log;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;

/**
 * 创建时间:2024/2/19
 * 功能描述:多种方法执行 adb shell 命令并返回结果
 * 注意:使用该类的方法,手机须拥有Root权限
 */
public class ShellUtil {
    public static String TAG = ShellUtil.class.getSimpleName();
    public static final String COMMAND_SU = "su";
    public static final String COMMAND_SH = "sh";

    /**
     * TODO 方法一:
     * 根据系统版本的类型执行命令行,返回执行结果
     * @param cmd 命令行字符串
     * @return 如果命令行有输出,则返回第一样输出,如果没有输出,则返回空字符串
     */
    public static String getExecuteCmdResult(String cmd) {
        if (Build.VERSION.INCREMENTAL.toLowerCase().contains("factory") || isUserDebugROM()) {
            String userDebugOSResult = executeCmdOnUserDebugOS(cmd);
            LOGI("getExecuteCmdResult(),当前系统版本为 factory 或 userdebug 类型,执行结果:" + userDebugOSResult);
            return userDebugOSResult;
        } else {
            String userRootOSResult = executeCmdOnUserRootOS(cmd);
            LOGI("getExecuteCmdResult(),当前系统版本为 userroot 类型,执行结果:" + userRootOSResult);
            return userRootOSResult;
        }
    }

    /**
     * 在 factory 或 userdebug 类型的版本中执行命令行,返回命令行结果
     * @param shellCommand
     * @return
     */
    public static String executeCmdOnUserDebugOS(String shellCommand) {
        LOGI("executeCmdOnUserDebugOS(),执行cmd:" + shellCommand);
        String line = null;
        String stringBuffer = "->";
        try {
            String[] cmd = new String[]{"/system/bin/sh", "-c", shellCommand};
            Process proc = Runtime.getRuntime().exec(cmd);
            InputStream inputSt = proc.getInputStream();
            BufferedReader reader1 = new BufferedReader(new InputStreamReader(inputSt));
            stringBuffer = reader1.readLine();
            LOGI("executeCmdOnUserDebugOS(),执行结果:" + stringBuffer);
            inputSt.close();
            reader1.close();
            proc.destroy();
        } catch (IOException e) {
            LOGE("executeCmdOnUserDebugOS(),关闭流数据时发生IO异常:" + e);
        }

        return stringBuffer;
    }

    /**
     * 在 userroot 的类型版本中执行命令行,返回命令行结果
     * @param cmd 命令行字符串
     * @return 如果命令行有输出,则返回第一样输出,如果没有输出,则返回空字符串
     */
    public static String executeCmdOnUserRootOS(String cmd) {
        LOGI("executeCmdOnUserRootOS(),执行cmd:" + cmd);
        Process p = null;
        String line = null;
        InputStream is = null;
        BufferedReader reader = null;
        ProcessBuilder pb = null;
        String[] cmdStringArray = {"/system/xbin/su", "-c", cmd};
        try {
            List<String> cmdList = Arrays.asList(cmdStringArray);
            pb = new ProcessBuilder().redirectErrorStream(true).command(cmdList);
            p = pb.start();
            is = p.getInputStream();
            reader = new BufferedReader(new InputStreamReader(is));
            line = reader.readLine();
            LOGI("executeCmdOnUserRootOS(),执行结果:" + line);
        } catch (Exception e) {
            LOGE("executeCmdOnUserRootOS(),发生异常:" + e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    LOGE("executeCmdOnUserRootOS(),关闭缓冲流时发生IO异常:" + e);
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    LOGE("executeCmdOnUserRootOS(),关闭输入流时发生IO异常:" + e);
                }
            }
            if (p != null) {
                p.destroy();
            }
        }

        return ((line == null) ? "" : line);
    }

    /**
     * TODO 方法二:
     * 异步执行命令并返回进程引用,支持su/sh权限自动切换
     * @param command 要执行的命令
     * @return Process 进程引用,可用于控制进程生命周期
     * @throws IOException 当无法执行命令时抛出异常
     */
    public static Process executeCommandAsRootAsync(String command) throws IOException {
        Process process = null;
        try {
            // 首先尝试使用su权限执行
            process = Runtime.getRuntime().exec(new String[]{COMMAND_SU, "-c", command});
            LOGI("executeCommandAsRootAsync(),使用su权限执行指令:" + command);
            return process;
        } catch (IOException e) {
            LOGE("executeCommandAsRootAsync(),使用su权限执行失败:" + e.getMessage());
            // 如果su权限失败,尝试使用sh权限执行
            try {
                if (process != null) {
                    process.destroy();
                }
                process = Runtime.getRuntime().exec(new String[]{COMMAND_SH, "-c", command});
                LOGI("executeCommandAsRootAsync(),使用sh权限执行指令:" + command);
                return process;
            } catch (IOException e2) {
                LOGE("executeCommandAsRootAsync(),使用sh权限执行也失败了:" + e2.getMessage());
                if (process != null) {
                    process.destroy();
                }
                throw e2; // 抛出sh权限执行失败的异常
            }
        }
    }

    /**
     * 异步执行命令并返回进程引用和输出结果,支持su/sh权限自动切换
     * @param command 要执行的命令
     * @param inProcess 用于接收进程引用和输出结果的回调接口
     * @throws IOException 当无法执行命令时抛出异常
     * @throws InterruptedException 当线程被中断时抛出异常
     */
    public static void executeCommandAsRootAsyncWithOutput(String command, InProcess inProcess) throws IOException, InterruptedException {
        Process process = null;
        try {
            // 首先尝试使用su权限执行
            process = Runtime.getRuntime().exec(new String[]{COMMAND_SU, "-c", command});
            LOGI("executeCommandAsRootAsyncWithOutput(),使用su权限执行指令:" + command);
        } catch (IOException e) {
            LOGE("executeCommandAsRootAsyncWithOutput(),使用su权限执行失败:" + e.getMessage());
            // 如果su权限失败,尝试使用sh权限执行
            try {
                if (process != null) {
                    process.destroy();
                }
                process = Runtime.getRuntime().exec(new String[]{COMMAND_SH, "-c", command});
                LOGI("executeCommandAsRootAsyncWithOutput(),使用sh权限执行指令:" + command);
            } catch (IOException e2) {
                LOGE("executeCommandAsRootAsyncWithOutput(),使用sh权限执行也失败了:" + e2.getMessage());
                if (process != null) {
                    process.destroy();
                }
                throw e2; // 抛出sh权限执行失败的异常
            }
        }

        // 将进程引用传递给回调
        if (inProcess != null) {
            inProcess.getProcess(process);
        }

        // 读取输出流
        if (process != null) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null && !Thread.currentThread().isInterrupted()) {
                if (inProcess != null) {
                    inProcess.parseMsg(line);
                }
                LOGI("executeCommandAsRootAsyncWithOutput(),执行结果:" + line);
            }
            reader.close();
        }
    }

    /**
     * TODO 方法三:
     * 使用不同权限执行adb命令,并返回命令执行结果
     * @param command
     * @return
     */
    public static String executeCommandAsRoot(String command) {
        StringBuilder output = new StringBuilder();

        // 尝试以 su 权限执行命令
        CommandResult suResult = executeCommandWithPermissions(command, COMMAND_SU);
        if (suResult.getResultCode() == 0) {
            LOGI("executeCommandAsRoot(),su 权限执行成功");
            return suResult.getOutput();
        } else {
            LOGE("executeCommandAsRoot(),su 权限执行失败,error code: " + suResult.getResultCode());
        }

        // 如果 su 权限执行失败,尝试以 sh 权限执行命令
        CommandResult shResult = executeCommandWithPermissions(command, COMMAND_SH);
        if (shResult.getResultCode() == 0) {
            LOGI("executeCommandAsRoot(),sh 权限执行成功");
            return shResult.getOutput();
        } else {
            LOGE("executeCommandAsRoot(),sh 权限执行失败,error code: " + shResult.getResultCode());
            output.append("Error: ").append(shResult.getError());
        }

        return output.toString();
    }

    private static CommandResult executeCommandWithPermissions(String command, String permission) {
        StringBuilder output = new StringBuilder();
        StringBuilder error = new StringBuilder();
        Process process = null;
        BufferedReader reader = null;

        try {
            process = Runtime.getRuntime().exec(new String[]{permission, "-c", command});
            processBuilderRedirectErrorStream(process);
            LOGI(permission + " --- executeCommandWithPermissions(),执行指令:" + command);

            // 读取输出流
            reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
                LOGI("使用 " + permission + "权限 --- executeCommandWithPermissions(),正确结果:" + line);
            }

            // 读取错误流
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            while ((line = errorReader.readLine()) != null) {
                error.append(line).append("\n");
                LOGE("使用 " + permission + "权限 --- executeCommandWithPermissions(),错误结果:" + line);
            }

            int responseCode = process.waitFor();
            return new CommandResult(responseCode, output.toString(), error.toString());
        } catch (IOException | InterruptedException e) {
            LOGE(permission + " --- executeCommandWithPermissions(),执行失败:" + e.getMessage());
            error.append("Error: ").append(e.getMessage());
            return new CommandResult(-1, output.toString(), error.toString());
        } finally {
            closeResources(process, reader);
        }
    }

    private static void processBuilderRedirectErrorStream(Process process) {
        try {
            process.getErrorStream().close(); // 关闭错误流
            process.getOutputStream().close(); // 关闭输出流
        } catch (IOException e) {
            LOGE("processBuilderRedirectErrorStream(),发生异常:" + e.getMessage());
        }
    }

    private static void closeResources(Process process, BufferedReader reader) {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                LOGE("Close BufferedReader Error: " + e.getMessage());
            }
        }
        if (process != null) {
            process.destroy();
        }
    }

    /**
     * TODO 方法四:
     * 执行adb命令,并返回执行结果
     * @param cmd
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public static String getExecCmdResult(String cmd) throws IOException, InterruptedException {
        StringBuilder result = new StringBuilder();
        DataOutputStream lDataOutputStream = null;
        DataInputStream lDataInputStream = null;
        Process lProcess = null;
        try {
            lProcess = Runtime.getRuntime().exec(COMMAND_SU);
        } catch (IOException pE) {
            lProcess = Runtime.getRuntime().exec(COMMAND_SH);
        }

        lDataOutputStream = new DataOutputStream(lProcess.getOutputStream());
        lDataInputStream = new DataInputStream(lProcess.getInputStream());
        BufferedReader lBufferedReader = new BufferedReader(new InputStreamReader(lDataInputStream));
        lDataOutputStream.writeBytes(cmd + "\n");
        LOGI("getExecCmdResult(),execute cmd: " + cmd);

        lDataOutputStream.writeBytes("exit\n");
        lDataOutputStream.flush();
        String line = null;
        while ((line = lBufferedReader.readLine()) != null && !Thread.currentThread().isInterrupted()) {
            result.append(line);
            LOGI("getExecCmdResult(),execute result:" + result);
        }
        lProcess.waitFor();
        if (lDataInputStream != null) {
            lDataOutputStream.close();
        }
        if (lDataInputStream != null) {
            lDataInputStream.close();
        }
        LOGI(result.toString());
        return result.toString();
    }

    /**
     * TODO 方法五:
     * 解析命令执行结果
     */
    public static void parseCmdResult(String cmd, InCmdMsg pInCmdMsg) throws IOException, InterruptedException {
        DataOutputStream lDataOutputStream = null;
        DataInputStream lDataInputStream = null;
        Process lProcess = null;
        try {
            lProcess = Runtime.getRuntime().exec(COMMAND_SU);
        } catch (IOException pE) {
            lProcess = Runtime.getRuntime().exec(COMMAND_SH);
        }
        lDataOutputStream = new DataOutputStream(lProcess.getOutputStream());
        lDataInputStream = new DataInputStream(lProcess.getInputStream());
        BufferedReader lBufferedReader = new BufferedReader(new InputStreamReader(lDataInputStream));
        lDataOutputStream.writeBytes(cmd + "\n");
        LOGI("parseCmdResult(),execute cmd: " + cmd);
        lDataOutputStream.writeBytes("exit\n");
        lDataOutputStream.flush();
        String line = null;
        while ((line = lBufferedReader.readLine()) != null && !Thread.currentThread().isInterrupted()) {
            if (pInCmdMsg!= null){
                pInCmdMsg.parseMsg(line);
            }
        }
        lProcess.waitFor();
        if (lDataInputStream != null) {
            lDataOutputStream.close();
        }
        if (lDataInputStream != null) {
            lDataInputStream.close();
        }
    }

    /**
     * TODO 方法六:
     * 解析命令执行结果
     */
    public static void parseCmdProcess(String cmd, InProcess pInProcess) throws IOException, InterruptedException {
        DataOutputStream lDataOutputStream = null;
        DataInputStream lDataInputStream = null;
        Process lProcess = null;
        try {
            lProcess = Runtime.getRuntime().exec(COMMAND_SU);
        } catch (IOException pE) {
            lProcess = Runtime.getRuntime().exec(COMMAND_SH);
        }
        if (pInProcess!= null){
            pInProcess.getProcess(lProcess);
        }
        lDataOutputStream = new DataOutputStream(lProcess.getOutputStream());
        lDataInputStream = new DataInputStream(lProcess.getInputStream());
        BufferedReader lBufferedReader = new BufferedReader(new InputStreamReader(lDataInputStream));
        lDataOutputStream.writeBytes(cmd + "\n");
        LOGI("parseCmdResult(),execute cmd: " + cmd);
        lDataOutputStream.writeBytes("exit\n");
        lDataOutputStream.flush();
        String line = null;
        while ((line = lBufferedReader.readLine()) != null && !Thread.currentThread().isInterrupted()) {
            if (pInProcess!= null){
                pInProcess.parseMsg(line);
            }
        }
        lProcess.waitFor();
        if (lDataInputStream != null) {
            lDataOutputStream.close();
        }
        if (lDataInputStream != null) {
            lDataInputStream.close();
        }
    }

    /**
     * TODO 方法七:
     * 执行命令,并把结果写入文件
     */
    public static void getExecCmdResultToFile(String cmd, String fileName, boolean append) throws IOException, InterruptedException {
        DataOutputStream lDataOutputStream = null;
        DataInputStream lDataInputStream = null;
        Process lProcess = null;
        try {
            lProcess = Runtime.getRuntime().exec(COMMAND_SU);
        } catch (IOException pE) {
            lProcess = Runtime.getRuntime().exec(COMMAND_SH);
        }
        File lFile = new File(fileName);
        if (!lFile.exists()) {
            lFile.createNewFile();
        }
        OutputStream lOutputStream = null;
        try {
            lOutputStream = new FileOutputStream(lFile, append);
        } catch (FileNotFoundException pE) {

        }
        lDataOutputStream = new DataOutputStream(lProcess.getOutputStream());
        lDataInputStream = new DataInputStream(lProcess.getInputStream());
        BufferedReader lBufferedReader = new BufferedReader(new InputStreamReader(lDataInputStream));
        lDataOutputStream.writeBytes(cmd + "\n");
        LOGI("exec: " + cmd);
        lDataOutputStream.writeBytes("exit\n");
        lDataOutputStream.flush();
        String line = null;
        while ((line = lBufferedReader.readLine()) != null && !Thread.currentThread().isInterrupted()) {
            lOutputStream.write(line.getBytes());
            lOutputStream.write("\r\n".getBytes());
        }
        lProcess.waitFor();
        if (lDataInputStream != null) {
            lDataOutputStream.close();
        }
        if (lDataInputStream != null) {
            lDataInputStream.close();
        }
    }

    /**  是否为 userdebug 类型版本 */
    public static boolean isUserDebugROM() {
        String fingerPrint = executeCmdOnUserDebugOS("getprop ro.product.build.fingerprint");
        if (fingerPrint.toLowerCase().contains("userdebug")) {
            LOGI("isUserDebugROM():true");
            return true;
        } else {
            LOGI("isUserDebugROM():false");
            return false;
        }
    }

    public static void LOGI(String msg) {
        Log.i(TAG, msg);
    }

    public static void LOGE(String msg) {
        Log.e(TAG, msg);
    }

    public static interface InCmdMsg{
        void parseMsg(String msg);
    }

    public static interface InProcess{
        void getProcess(Process pProcess);
        void parseMsg(String msg);
    }

    // 定义 CommandResult 类
    public static class CommandResult {
        private final int resultCode;
        private final String output;
        private final String error;

        public CommandResult(int resultCode, String output, String error) {
            this.resultCode = resultCode;
            this.output = output;
            this.error = error;
        }

        public int getResultCode() {
            return resultCode;
        }

        public String getOutput() {
            return output;
        }

        public String getError() {
            return error;
        }
    }
}

3、调用示例

import android.util.Log;
import 你的包名.UiAutomatorUtil;
import 你的包名.UiXmlParser;
import 你的包名.util.ShellUtil;
import java.io.File;

/**
 * Description:自动化测试操作类
 */
public class TestAutomator {
    private static final String TAG = TestAutomator.class.getSimpleName();
    private static final String testUiXmlPath = "/sdcard/UiDump.xml";
    private static final long TEST_DURATION = 5 * 1000;   // 运行时长

    /** APP 自动化测试操作 */
    public static boolean runStressTest() {
        Log.i(TAG, "runStressTest(), 自动化测试操作,已开始");
        try {
            // 1、启动APP 
            ShellUtil.getExecCmdResult("am start -n 应用包名/启动Activity");
            Thread.sleep(2000); // 等待启动

            // 2、点击"Consumer IR"
            boolean consumerIRResult = UiAutomatorUtil.clickByText(TAG, "Consumer IR", testUiXmlPath);
            if (!consumerIRResult) {
                Log.e(TAG, "runStressTest(),未能找到或点击'Consumer IR'按钮");
                return false;
            }
            Thread.sleep(1000); // 等待跳转

            // 3、在输入框输入 "100"
            boolean inputResult = UiAutomatorUtil.inputText("100");
            if (!inputResult) {
                Log.e(TAG, "runStressTest(),未能在输入框中输入文本");
                return false;
            }
            Thread.sleep(500); // 等待输入完成

            // 4、点击"Power"
            boolean powerResult = UiAutomatorUtil.clickByText(TAG,"Power", testUiXmlPath);
            if (!powerResult) {
                Log.e(TAG, "runStressTest(),未能找到或点击'Power'按钮");
                return false;
            }

            // 5、等待测试完成
            Thread.sleep(TEST_DURATION);

            // 6、退出应用
            ShellUtil.getExecCmdResult("am force-stop 应用包名");
            Log.i(TAG, "runStressTest(),APP 自动化测试操作,已完成");
            return true;
        } catch (Exception e) {
            Log.e(TAG, "runStressTest(),APP 自动化测试操作时发生异常: " + e.getMessage());
            return false;
        }
    }

}
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐