MonkeyPanel.java

package edu.jiangxin.apktoolbox.android.monkey;

import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
import edu.jiangxin.apktoolbox.swing.extend.NumberPlainDocument;
import edu.jiangxin.apktoolbox.utils.Constants;
import edu.jiangxin.apktoolbox.utils.DateUtils;
import org.apache.commons.io.IOUtils;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author jiangxin
 * @author 2019-04-12
 *
 */
public class MonkeyPanel extends EasyPanel {

    @Serial
    private static final long serialVersionUID = 1L;
    
    private static final String LOG_NAME = "java_monkey_log";

    private static final String HOUR = "时";
    private static final String MINUTE = "分";
    private static final String SECOND = "秒";

    private static final String CMD = "cmd.exe";

    private static final String CMD_PS_A = "adb -s ";
    private static final String CMD_PS_B = " shell ps";
    private static final String CMD_KILL_A = "adb -s ";
    private static final String CMD_KILL_B = " shell kill ";

    private static final String MONKEY = "com.android.commands.monkey";

    private static final String TRANSACTIONCOUNT = "9999";

    /**
     * 设备列表不能为空!
     */
    private static final String MSG4 = "设备列表不能为空!请[刷新]!";

    /**
     * 应用程序不能为空!
     */
    private static final String MSG5 = "应用程序不能为空!请选其他设备!";

    /**
     * 事件间隔或时间数量不能为空!
     */
    private static final String MSG2 = "事件间隔或时间数量不能为空!";

    /**
     * 日志保存路径不能为空!
     */
    private static final String MSG6 = "日志保存路径不能为空!";

    Thread threadTimeType = null;
    Process monkeyProcess = null;

    JComboBox<String> comboBoxDevices = new JComboBox<>();
    JButton refreshButton = new JButton("刷新");

    JButton logPathButton = new JButton("选择路径");
    JButton executeButton = new JButton("运行命令");

    JButton resetButton = new JButton("重置页面");
    JButton interruptButton = new JButton("终止运行");

    JLabel labelHour = new JLabel();
    JLabel labelMinute = new JLabel();
    JLabel labelSecond = new JLabel();
    JTextField textMillisecond = new JTextField(30);
    JTextField textTime = new JTextField(30);
    JTextField textLogPath = new JTextField(90);

    JComboBox<String> comboBoxProgram = new JComboBox<>();
    JComboBox<String> comboBoxTime = new JComboBox<>();

    /**
     * --dbg-no-events:初始化启动的activity,但是不产生任何事件
     */
    JCheckBox checkBoxDbgNoEvents = new JCheckBox("初始化启动的activity,但是不产生任何事件");

    /**
     * --hprof:指定该项后在事件序列发送前后会立即生成分析报告(一般建议指定该项)
     */
    JCheckBox checkBoxHprof = new JCheckBox("在事件序列发送前后会立即生成分析报告");

    /**
     * --ignore-crashes:忽略崩溃
     */
    JCheckBox checkBoxCrashes = new JCheckBox("忽略崩溃", true);

    /**
     * --ignore-timeouts:忽略超时
     */
    JCheckBox checkBoxTimeouts = new JCheckBox("忽略超时", true);

    /**
     * --monitor-native-crashes:跟踪本地方法的崩溃问题
     */
    JCheckBox checkBoxNativeCrashes = new JCheckBox("跟踪本地方法的崩溃问题", true);

    /**
     * --ignore-security-exceptions:忽略安全异常
     */
    JCheckBox checkBoxExceptions = new JCheckBox("忽略安全异常", true);

    /**
     * --kill-process-after-error:发生错误后直接杀掉进程
     */
    JCheckBox checkBoxKill = new JCheckBox("发生错误后直接杀掉进程");

    /**
     * --wait-dbg:知道连接了调试器才执行monkey测试
     */
    JCheckBox checkBoxWaitDbg = new JCheckBox("停止Monkey执行,直到有调试器与其连接");

    ButtonGroup group = new ButtonGroup();

    /**
     * 缺省值
     */
    JRadioButton radioButton0 = new JRadioButton("基本信息");

    /**
     * 比较详细
     */
    JRadioButton radioButton1 = new JRadioButton("比较详细");

    /**
     * 非常详细(默认选中)
     */
    JRadioButton radioButton2 = new JRadioButton("非常详细", true);

    ArrayList<String> list;
    int flag = 0;
    String[] monkeyCmd = null;

    @Override
    public void initUI() {
        setPreferredSize(new Dimension(Constants.DEFAULT_PANEL_WIDTH, Constants.DEFAULT_PANEL_HEIGHT));
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

        JPanel devicesPanel = new JPanel();
        devicesPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        initDevices(devicesPanel);
        add(devicesPanel);

        JPanel programPanel = new JPanel();
        programPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        initProgram(programPanel);
        add(programPanel);

        JPanel restrainConditionPanel = new JPanel();
        restrainConditionPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        initRestrainCondition(restrainConditionPanel);
        add(restrainConditionPanel);

        JPanel eventIntervalPanel = new JPanel();
        eventIntervalPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        initEventInterval(eventIntervalPanel);
        add(eventIntervalPanel);

        JPanel runTimePanel = new JPanel();
        runTimePanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        initRunTime(runTimePanel);
        add(runTimePanel);

        JPanel logLevelPanel = new JPanel();
        logLevelPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        initLogLevel(logLevelPanel);
        add(logLevelPanel);

        JPanel logPathPanel = new JPanel();
        logPathPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        initLogPath(logPathPanel);
        add(logPathPanel);

        JPanel operationPanel = new JPanel();
        operationPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        initOperation(operationPanel);
        add(operationPanel);
    }

    private void initDevices(JPanel panel) {
        JLabel labelDevices = new JLabel("设备列表");
        labelDevices.setPreferredSize(new Dimension(60, 25));
        comboBoxDevices.setPreferredSize(new Dimension(240, 25));
        refreshButton.setPreferredSize(new Dimension(60, 25));

        panel.add(labelDevices);
        panel.add(comboBoxDevices);
        panel.add(refreshButton);

        refreshButton.addActionListener(e -> {
            comboBoxDevices.removeAllItems();
            List<String> devices = getDevices();
            for (String device : devices) {
                comboBoxDevices.addItem(device);
            }
        });
    }

    private void initProgram(JPanel panel) {
        JLabel labelProgram = new JLabel("应用程序");
        labelProgram.setPreferredSize(new Dimension(60, 25));
        comboBoxProgram.setPreferredSize(new Dimension(240, 25));
        comboBoxProgram.setEditable(true);
        comboBoxProgram.setSelectedItem("com.shinow.*");

        panel.add(labelProgram);
        panel.add(comboBoxProgram);

        comboBoxDevices.addActionListener(e -> {
            comboBoxProgram.removeAllItems();
            comboBoxProgram.setSelectedItem("com.shinow.*");
            List<String> programs = getApplication(comboBoxDevices.getSelectedItem().toString());
            for (String program : programs) {
                comboBoxProgram.addItem(program);
            }

        });
    }

    private void initRestrainCondition(JPanel panel) {
        JLabel labelRestrain = new JLabel("约束条件");
        labelRestrain.setPreferredSize(new Dimension(60, 25));
        checkBoxCrashes.setPreferredSize(new Dimension(100, 25));
        checkBoxTimeouts.setPreferredSize(new Dimension(100, 25));
        checkBoxExceptions.setPreferredSize(new Dimension(110, 25));
        checkBoxNativeCrashes.setPreferredSize(new Dimension(180, 25));
        checkBoxKill.setPreferredSize(new Dimension(180, 25));
        checkBoxWaitDbg.setPreferredSize(new Dimension(280, 25));
        checkBoxHprof.setPreferredSize(new Dimension(280, 25));

        panel.add(labelRestrain);
        panel.add(checkBoxCrashes);
        panel.add(checkBoxTimeouts);
        panel.add(checkBoxExceptions);
        panel.add(checkBoxNativeCrashes);
        panel.add(checkBoxKill);
        panel.add(checkBoxWaitDbg);
        panel.add(checkBoxHprof);
    }

    private void initEventInterval(JPanel panel) {
        JLabel labelSpace = new JLabel("事件间隔");
        labelSpace.setPreferredSize(new Dimension(60, 25));
        textMillisecond.setPreferredSize(new Dimension(150, 25));
        textMillisecond.setDocument(new NumberPlainDocument(7));
        textMillisecond.setText("500");
        JLabel labelMillisecond = new JLabel("毫秒");
        labelMillisecond.setPreferredSize(new Dimension(40, 25));

        panel.add(labelSpace);
        panel.add(textMillisecond);
        panel.add(labelMillisecond);
    }

    private void initRunTime(JPanel panel) {
        JLabel labelTime = new JLabel("运行时长");
        labelTime.setPreferredSize(new Dimension(60, 25));
        textTime.setPreferredSize(new Dimension(150, 25));
        textTime.setDocument(new NumberPlainDocument(7));
        textTime.setText("1");
        comboBoxTime.setPreferredSize(new Dimension(60, 25));
        comboBoxTime.addItem(HOUR);
        comboBoxTime.addItem(MINUTE);
        comboBoxTime.addItem(SECOND);

        labelHour.setPreferredSize(new Dimension(50, 25));
        labelMinute.setPreferredSize(new Dimension(50, 25));
        labelSecond.setPreferredSize(new Dimension(50, 25));

        panel.add(labelTime);
        panel.add(textTime);
        panel.add(comboBoxTime);

        panel.add(labelHour);
        panel.add(labelMinute);
        panel.add(labelSecond);
    }

    private void initLogLevel(JPanel panel) {
        JLabel labelLogLevel = new JLabel("日志级别");
        labelLogLevel.setPreferredSize(new Dimension(60, 25));

        group.add(radioButton0);
        group.add(radioButton1);
        group.add(radioButton2);

        radioButton0.setPreferredSize(new Dimension(80, 25));
        radioButton1.setPreferredSize(new Dimension(80, 25));
        radioButton2.setPreferredSize(new Dimension(80, 25));

        panel.add(labelLogLevel);
        panel.add(radioButton0);
        panel.add(radioButton1);
        panel.add(radioButton2);
    }

    private void initLogPath(JPanel panel) {
        logPathButton.setPreferredSize(new Dimension(100, 25));
        textLogPath.setColumns(60);
        textLogPath.setEditable(false);
        textLogPath.setText("D:");

        panel.add(logPathButton);
        panel.add(textLogPath);

        logPathButton.addActionListener(e -> {
            if (e.getSource() == logPathButton) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                int intRetVal = fileChooser.showOpenDialog(new Label());
                if (intRetVal == JFileChooser.APPROVE_OPTION) {
                    textLogPath.setText(fileChooser.getSelectedFile().getPath());
                }
            }
        });
    }

    private void initOperation(JPanel panel) {
        executeButton.setPreferredSize(new Dimension(100, 25));
        resetButton.setPreferredSize(new Dimension(100, 25));
        interruptButton.setPreferredSize(new Dimension(100, 25));

        panel.add(executeButton);
        panel.add(resetButton);
        panel.add(interruptButton);

        interruptButton.setEnabled(false);

        executeButton.addActionListener(e -> execute());

        resetButton.addActionListener(e -> reset());

        interruptButton.addActionListener(e -> interrupt());

    }

    private void execute() {
        String combinedOptionCmd = getCombinedOptionCmd();

        boolean isOk = checkCondition();
        if (!isOk) {
            return;
        }

        String logLevel = queryLogLevelString();
        String logFile = textLogPath.getText() + "\\" + LOG_NAME + DateUtils.getCurrentDateString() + ".txt";

        monkeyCmd = new String[] { CMD, "/C",
                "adb -s " + comboBoxDevices.getSelectedItem() + " shell monkey -p "
                        + comboBoxProgram.getSelectedItem() + combinedOptionCmd + " --throttle "
                        + textMillisecond.getText() + "" + logLevel + "" + TRANSACTIONCOUNT + " > " + logFile };

        logger.info("Monkey: {}", monkeyCmd[2]);

        flag = 0;

        try {
            monkeyProcess = Runtime.getRuntime().exec(monkeyCmd);
        } catch (Exception ex) {
            logger.error("Exception", ex);
        }

        executeButton.setEnabled(false);
        resetButton.setEnabled(false);
        interruptButton.setEnabled(true);

        // 倒计时文本
        labelHour.setVisible(true);
        labelMinute.setVisible(true);
        labelSecond.setVisible(true);

        // 获取时间类型
        String timeType = (String) comboBoxTime.getSelectedItem();
        long time = Long.parseLong(textTime.getText());
        if (Objects.equals(timeType, HOUR)) {
            time *= 3600;
        } else if (Objects.equals(timeType, MINUTE)) {
            time *= 60;
        }
        threadTimeType = new Thread(new CountdownRunnable(time));
        threadTimeType.start();
    }

    private void reset() {
        // 重置约束条件
        checkBoxCrashes.setSelected(true);
        checkBoxTimeouts.setSelected(true);
        checkBoxExceptions.setSelected(true);
        checkBoxNativeCrashes.setSelected(true);
        // 重置日志级别
        radioButton0.setSelected(false);
        radioButton1.setSelected(false);
        radioButton2.setSelected(true);
        // 重置事件间隔
        textMillisecond.setText("500");
        // 重置运行时长
        textTime.setText("1");
    }

    private void interrupt() {
        // 杀掉Monkey执行进程
        interruptThread();
        executeButton.setEnabled(true);
        resetButton.setEnabled(true);
        interruptButton.setEnabled(false);
    }

    private String getCombinedOptionCmd() {
        String ignoreCrashes = checkBoxCrashes.isSelected() ? " --ignore-crashes" : "";
        String ignoreTimeouts = checkBoxTimeouts.isSelected() ? " --ignore-timeouts" : "";
        String monitorNativeCrashes = checkBoxNativeCrashes.isSelected() ? " --monitor-native-crashes" : "";
        String ignoreSecurityExceptions = checkBoxExceptions.isSelected() ? " --ignore-security-exceptions" : "";
        String hprof = checkBoxHprof.isSelected() ? " --hprof" : "";
        String killProcessAfterError = checkBoxKill.isSelected() ? " --kill-process-after-error" : "";
        String waitDbg = checkBoxWaitDbg.isSelected() ? " --wait-dbg" : "";
        String dbgNoEvents = checkBoxDbgNoEvents.isSelected() ? "--dbg-no-events" : "";
        return ignoreCrashes + ignoreTimeouts + ignoreSecurityExceptions + monitorNativeCrashes + hprof
                + killProcessAfterError + waitDbg + dbgNoEvents;
    }

    private boolean checkCondition() {
        // 判断设备列表和应用程序是否为空
        if (comboBoxDevices.getItemCount() == 0) {
            new MyDialog(MSG4).setVisible(true);
            refreshButton.requestFocus();
            return false;
        }
        if (comboBoxProgram.getItemCount() == 0) {
            new MyDialog(MSG5).setVisible(true);
            return false;
        }
        // 判断事件间隔和运行时长是否为空
        if (textMillisecond.getText().length() == 0) {
            new MyDialog(MSG2).setVisible(true);
            textMillisecond.requestFocus();
            return false;
        }
        if (textTime.getText().length() == 0) {
            new MyDialog(MSG2).setVisible(true);
            textTime.requestFocus();
            return false;
        }
        if (textLogPath.getText().length() == 0) {
            new MyDialog(MSG6).setVisible(true);
            logPathButton.requestFocus();
            return false;
        }
        return true;
    }

    private String queryLogLevelString() {
        if (radioButton0.isSelected()) {
            return " -v ";
        } else if (radioButton1.isSelected()) {
            return " -v -v ";
        } else if (radioButton2.isSelected()) {
            return " -v -v -v ";
        }
        return "";
    }

    /**
     * 中断Monkey命令
     */
    public void interruptThread() {

        logger.info("中断Monkey命令--开始");

        String[] cmd1 = new String[] { CMD, "/c", CMD_PS_A + comboBoxDevices.getSelectedItem() + CMD_PS_B };
        executeCommand(cmd1, MONKEY);

        List<String> listPid = list;
        logger.info("获取的中断Monkey进程数量:" + listPid.size());

        String[] cmd2 = null;
        String pid = "";
        for (String s : listPid) {
            pid = s;
            cmd2 = new String[]{CMD, "/c", CMD_KILL_A + comboBoxDevices.getSelectedItem() + CMD_KILL_B + pid};
            executeCommand(cmd2, MONKEY);
        }

        flag = 1;

        monkeyProcess.destroy();

        labelHour.setVisible(false);
        labelMinute.setVisible(false);
        labelSecond.setVisible(false);

        logger.info("中断Monkey命令--结束");

    }

    private List<String> getDevices() {
        List<String> devices = new ArrayList<>();
        logger.info("get device list start");
        Process process = null;
        try {
            process = Runtime.getRuntime().exec("adb devices");
        } catch (IOException e) {
            logger.error("exec command failed: " + e.getMessage());
        }
        if (process == null) {
            logger.error("process is null");
            return devices;
        }
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.contains("device") && !line.contains("List of")) {
                    line = line.substring(0, line.length() - 7);
                    logger.info("device name: " + line);
                    devices.add(line);
                }
            }
        } catch (IOException e) {
            logger.error("read failed: " + e.getMessage());
        } finally {
            IOUtils.closeQuietly(process.getOutputStream());
            IOUtils.closeQuietly(process.getErrorStream());
            IOUtils.closeQuietly(process.getInputStream());
        }
        logger.info("get device list end");
        return devices;
    }

    private List<String> getApplication(String device) {
        List<String> apps = new ArrayList<>();
        logger.info("get application list start");
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(new String[]{"adb", "-s", device, "shell", "pm", "list", "packages"});
        } catch (IOException e) {
            logger.error("exec command failed: " + e.getMessage());
        }
        if (process == null) {
            logger.error("process is null");
            return apps;
        }
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.contains("package:")) {
                    logger.info("application name:" + line);
                    apps.add(line.replace("package:", ""));
                }
            }
        } catch (IOException e) {
            logger.error("read failed: " + e.getMessage());
        } finally {
            IOUtils.closeQuietly(process.getOutputStream());
            IOUtils.closeQuietly(process.getErrorStream());
            IOUtils.closeQuietly(process.getInputStream());
        }
        logger.info("get application list end");
        return apps;
    }

    private void executeCommand(String[] cmd, String keyValue) {
        logger.info("exec cmd start");
        list = new ArrayList<>();
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(cmd);
        } catch (IOException e) {
            logger.error("exec command failed: " + e.getMessage());
        }
        if (process == null) {
            logger.error("process is null");
            return;
        }
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.contains(keyValue)) {
                    line = line.replace(" ", ":");
                    System.out.println("[" + keyValue + "][" + line + "]");
                    String[] str = line.split(":");
                    System.out.println(str.length);
                    for (int i = 1; i < str.length; i++) {
                        if (str[i].length() > 0) {
                            logger.info("Pid: " + str[i]);
                            list.add(str[i]);
                            break;
                        }
                    }
                }
            }
        } catch (IOException e) {
            logger.error("read failed: " + e.getMessage());
        } finally {
            IOUtils.closeQuietly(process.getOutputStream());
            IOUtils.closeQuietly(process.getErrorStream());
            IOUtils.closeQuietly(process.getInputStream());
        }
        logger.info("exec cmd end");
    }

    /**
     * 倒计时
     * 
     */
    class CountdownRunnable implements Runnable {
        private long times;

        public CountdownRunnable(long times) {
            this.times = times;
        }

        @Override
        public void run() {
            // 自定义倒计时时间
            long time = times;
            long hour = 0;
            long minute = 0;
            long seconds = 0;

            if (time >= 0) {
                for (int i = 0; i <= time; i++) {
                    // 监控Monkey命令
                    monitorMonkey(time, MONKEY);
                    // 监控Monkey命令操作的App
                    monitorApp(time, comboBoxProgram.getSelectedItem().toString());
                    // 判断标识,是否中断操作
                    System.out.println("flag==1?Interrupt:Continue:" + flag);
                    if (flag == 1) {
                        break;
                    }
                    hour = time / 3600;
                    minute = (time - hour * 3600) / 60;
                    seconds = time - hour * 3600 - minute * 60;
                    labelHour.setText(hour + HOUR);
                    labelMinute.setText(minute + MINUTE);
                    labelSecond.setText(seconds + SECOND);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        logger.error("InterruptedException {}", e.getMessage());
                        Thread.currentThread().interrupt();
                    }
                    // 正常结束
                    if (time == 0) {
                        Thread.currentThread().interrupt();
                        interruptThread();
                        executeButton.setEnabled(true);
                        resetButton.setEnabled(true);
                        interruptButton.setEnabled(false);
                    }
                    i--;
                    time--;
                }
                if (flag == 1) {
                    Thread.currentThread().interrupt();
                    threadTimeType.interrupt();
                    executeButton.setEnabled(true);
                    resetButton.setEnabled(true);
                    interruptButton.setEnabled(false);
                }
            }
        }

        /**
         * 间隔60秒监控一次运行monkey命令是否为运行状态,或已关闭
         * 
         * @param time     当前剩余执行时间
         * @param keyValue Monkey命令的进程名称
         */
        private void monitorMonkey(long time, String keyValue) {
            if ((time - 1) % 120 == 0) {
                logger.info("监控[" + keyValue + "]线程是否执行完毕---开始");
                logger.info("每60秒监听一次,此时time的值:" + (time - 1));
                String[] cmd = new String[] { CMD, "/c",
                        CMD_PS_A + comboBoxDevices.getSelectedItem() + CMD_PS_B };
                logger.info("当前命令:" + cmd[2]);
                executeCommand(cmd, keyValue);
                logger.info("当前线程数:" + list.size());
                if (list.size() == 0) {
                    String log = textLogPath.getText();
                    System.out.println(textLogPath.getText());
                    String getDate = DateUtils.getCurrentDateString();
                    // 日志文件路径
                    String logFile = log.substring(0, log.indexOf("java_monkey_log")) + LOG_NAME + getDate + ".txt";
                    textLogPath.setText(logFile);
                    System.out.println("monkeyCmd[2];" + monkeyCmd[2]);
                    monkeyCmd[2] = monkeyCmd[2].substring(0, monkeyCmd[2].indexOf("java_monkey_log"))
                            + "java_monkey_log" + getDate + ".txt";
                    logger.info("Monkey命令已经停止,再次执行");
                    logger.info("monkeyCmd[2];" + monkeyCmd[2]);
                    String[] monkeyCommand = new String[] { CMD, "/c", monkeyCmd[2] };
                    logger.info("执行");
                    // 再次执行
                    try {
                        monkeyProcess = Runtime.getRuntime().exec(monkeyCommand);
                    } catch (Exception e) {
                        logger.error("Exception", e);
                    }
                }
                logger.info("监控[" + keyValue + "]线程是否执行完毕---结束");
            }
        }

        /**
         * 间隔60秒监控一次运行app是否是启动状态,或崩溃掉
         * 
         * @param time     当前剩余执行时间
         * @param keyValue App的进程名称
         */
        private void monitorApp(long time, String keyValue) {
            if ((time - 1) % 120 == 0) {
                logger.info("监控[" + keyValue + "]线程是否执行完毕---开始");
                logger.info("每60秒监听一次,此时time的值:" + (time - 1));
                String[] cmd = new String[] { CMD, "/c",
                        CMD_PS_A + comboBoxDevices.getSelectedItem() + CMD_PS_B };
                logger.info("当前命令:" + cmd[2]);
                executeCommand(cmd, keyValue);
                logger.info("当前线程数:" + list.size());
                if (list.size() == 0) {
                    logger.info("[" + keyValue + "]已经关闭或崩溃,无法继续执行Monkey,退出系统");
                    logger.info("监控[" + keyValue + "]线程是否执行完毕---结束");
                    // 杀掉Monkey执行进程
                    interruptThread();
                    // System.gc();
                }
                logger.info("监控[" + keyValue + "]线程是否执行完毕---结束");
            }
        }
    }

    static class MyDialog extends JDialog {

        @Serial
        private static final long serialVersionUID = 1L;

        MyDialog(String msg3) {
            super((Frame)null, "提示", true);
            setSize(320, 180);
            Container container = getContentPane();
            container.setLayout(null);
            JLabel jl = new JLabel(msg3);
            jl.setBounds(70, 1, 200, 100);
            JButton jbb = new JButton("确    定");
            jbb.setBounds(97, 80, 100, 25);
            container.add(jl);
            container.add(jbb);

            // 设置位置
            int w = (Toolkit.getDefaultToolkit().getScreenSize().width - 320) / 2;
            int h = (Toolkit.getDefaultToolkit().getScreenSize().height - 180) / 2;

            setLocation(w, h);

            jbb.addActionListener(e -> dispose());
            setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        }
    }
}