手记

编写可执行jar——java的Process类的使用(二)

你知道怎么在控制台使用ping吗?那你知道怎么在java中使用ping吗?

1.批处理文件

批处理文件大家一定不陌生。接触最多的应该就是tomcat中的start.bat或者start.sh了。bat是在windows环境下运行的批处理文件,sh则是linux的shell脚本。

2.adb指令

安卓刷机的小伙伴们肯定对adb不陌生吧?为了使用adb,我们先安装一个夜神模拟器。比如我安装在C:\Program Files\Nox\bin,那么我们先打开cmd,并且cd到C:\Program Files\Nox\bin目录中。adb指令列表可以看https://www.yeshen.com/faqs/H15tDZ6YW。我们先启动夜神模拟器。

启动完成后

我们此时在控制台输入adb devices查看启动的夜神模拟器列表

可以看到已经启动了一个模拟器,占用了62001的端口。

3.Process类

如何在java中执行上述的指令呢?java中有一个类叫做Process,用于执行shell之类的指令,而且可以获得控制台的输出内容。

public class App {

    public static void main(String...args)throws Exception{
        //执行指令
        Process process = Runtime.getRuntime().exec("adb devices");
        System.out.println("【控制台执行信息】");
        System.out.println(readInputstream(process.getInputStream()));
        System.out.println("【控制台错误信息】");
        System.out.println(readInputstream(process.getErrorStream()));

    }

    /**
     * 读取控制台打印的文字
     * @param in
     * @return
     * @throws Exception
     */
    public static String readInputstream(InputStream in) throws Exception{
        InputStream inputStream = in;
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        StringBuilder sb = new StringBuilder();
        String line = null;
        while((line=bufferedReader.readLine())!=null){
            sb.append(line).append("\n");
        }
        return sb.toString();
    }
}

执行结果如下

这是因为在程序入口下找不到adb.exe这个文件。我们只要把【adb】换成【C:\Program Files\Nox\bin\adb】就可以了。

执行结果如下

还有一种执行指令的方式就比上述的方式更加简单了。

下面要讲的是ProcessBuilder这个类。我们将main函数修改成以下样子

    public static void main(String...args)throws Exception{
        //执行指令
//        Process process = Runtime.getRuntime().exec("C:\\Program Files\\Nox\\bin\\adb devices");
        //此处利用ProcessBuilder的command方法,
        //值得注意的是command方法支持可变参数,
        //也可以使用List,只要按指令空格分开好每个指令项就OK
        Process process = new ProcessBuilder().command("C:\\Program Files\\Nox\\bin\\adb","devices").start();
        System.out.println("【控制台执行信息】");
        System.out.println(readInputstream(process.getInputStream()));
        System.out.println("【控制台错误信息】");
        System.out.println(readInputstream(process.getErrorStream()));

    }

执行结果也是一样的。接下来我们也建一个Builder。

4.编写指令词典

可以看到,adb指令实在太多了。作为一个java程序员,是没必要去记住这些东西的,因为实际上我们并不常用这个东西。所以我们只好建一个指令词典了。指令词典的demo如下(我使用了lombok插件,自动生成Getter方法,不知道的同志们,请百度搜一下):

package com.zeemoo.nox.actuator.consts;

import lombok.Getter;

/**
 * 夜神模拟器的adb指令词典
 *
 * @author zhang.shushan
 * @date 2018/6/8
 */
@Getter
public class NoxCmdDict {

    /**
     * adb脚本指令
     */
    public final static String ADB = "adb";

    /**
     * 选择模拟器
     */
    public final static String SERVER_HOST = "-s";

    /**
     * 模拟器列表
     */
    public final static String DEVICES = "devices";

    /**
     * 安装apk
     */
    public final static String INSTALL = "install";

    /**
     * 从电脑发送文件到模拟器,没什么卵用,还有权限限制
     */
    @Deprecated
    public final static String PUSH = "push";

    /**
     * 从模拟器拉取文件到电脑,没什么卵用,还有权限限制
     */
    @Deprecated
    public final static String PULL = "pull";

    /**
     * 卸载app
     */
    public final static String UNINSTALL = "uninstall";

    /**
     * shell脚本指令,安卓内核为linux
     */
    public final static String SHELL = "shell";

    /**
     * 列举进程
     */
    public final static String SHELL_PS = "ps";

    /**
     * 包含某个字符串的进程信息
     */
    public final static String SHELL_PS_MTH_FIND_STR ="|findStr";

    /**
     * 包指令
     */
    public final static String SHELL_PM = "pm";

    /**
     * 包指令下的列举指令
     */
    public final static String SHELL_PM_LIST = "list";

    /**
     * 包指令下的列举指令选项,表示列举所有的包名
     */
    public final static String SHELL_PM_LIST_PACKAGES = "packages";

    /**
     * 包指令下的列举指令选项的附加选项,表示列举所有的包名和对应的路径
     */
    public final static String SHELL_PM_LIST_PACKAGES_OP_NAME_AND_PATH = "-f";

    /**
     * 获取某个应用的路径,需要填写包名
     */
    public final static String SHELL_PM_PATH = "path";

    /**
     * 清除应用缓存,后面接包名
     */
    public final static String SHELL_PM_CLEAR_TEMP = "clear";

    /**
     * 应用管理指令(Activity Manager),启动或关闭应用
     */
    public final static String SHELL_AM = "am";

    /**
     * 启动应用,最后接上包名+“/”+Activity类名
     */
    public final static String SHELL_AM_START = "start";

    /**
     * 这个选项表示如果应用启动了就直接打开后台进程,如果没启动则启动。
     * 不加此选项每次调用start的时候则每次都重启应用
     */
    public final static String SHELL_AM_START_OP_XSTART = "-n";

    /**
     * 关闭应用,后接包名
     */
    public final static String SHELL_AM_STOP = "force-stop";

    /**
     * 杀死进程,后接包名,似乎没什么用
     */
    public final static String SHELL_AM_KILL = "kill";

    /**
     * 杀死后台所有进程,似乎没什么用
     */
    public final static String SHELL_AM_KILL_ALL = "kill-all";

    /**
     * shell模拟输入
     */
    public final static String SHELL_INPUT = "input";

    /**
     * 模拟输入文字(不支持中文)
     */
    public final static String SHELL_INPUT_TEXT = "text";

    /**
     * 模拟点击按键
     */
    public final static String SHELL_INPUT_KEYEVENT = "keyevent";

    /**
     * 模拟鼠标点击,后面接X,Y
     */
    public final static String SHELL_INPUT_TAP = "tap";

    /**
     * 模拟鼠标滑动,后面接X1,Y1,X2,Y2
     */
    public final static String SHELL_INPUT_SWIPE = "swipe";

    /**
     * 截屏
     */
    public final static String SHELL_SCREEN_CAP = "screencap";

    /**
     * 截屏选项,后接截屏文件输出目标
     */
    public final static String SHELL_SCREEN_CAP_OP_DEST = "-p";

    /**
     * 退出shell
     */
    public final static String SHELL_EXIT = "exit";

    /**
     * 夜神模拟器的adb
     */
    public final static String NOX_ADB = "nox_adb";

    /**
     * 夜神模拟器属性设置
     */
    public final static String NOX_ADB_SHELL_SET_PROP = "setprop";

    /**
     * 设置手机的纬度
     */
    public final static String NOX_ADB_SHELL_SET_PROP_LATITUDE = "persist.nox.gps.latitude";

    /**
     * 设置模拟器的经度
     */
    public final static String NOX_ADB_SHELL_SET_PROP_LONGITUDE = "persist.nox.gps.longitude";

    /**
     * 设置模拟器的mac地址
     */
    public final static String NOX_ADB_SHELL_SET_PROP_MAC = "setprop persist.nox.wifimac";

}

现在来创建一个Builder类,代码如下

package com.zeemoo.nox.actuator.service;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

//此处使用静态引入指令词典,可以试着删去这一行,看看有什么区别
import static com.zeemoo.nox.actuator.consts.NoxCmdDict.*;

/**
 * 功能指令构建
 *
 * @author zhang.shushan
 * @date 2018/6/8
 */
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class NoxCmdBuilder {

    /**
     * 夜神模拟器的根目录
     */
    private String noxPath;

    /**
     * 给指令添加指定模拟器
     * @param cmds
     * @param host
     * @return
     */
    public List<String> addHost(List<String> cmds,String host){
        if(host!=null&&"".equals(host)){
            cmds.add(1, SERVER_HOST);
            cmds.add(2,host);
        }
        return cmds;
    }

    /**
     * 筛选字符进程
     * @param example
     * @return
     */
    public List<String> listProcess(String example){

        List<String> cmds=listPackageName();
        cmds.add(SHELL_PS_MTH_FIND_STR);
        cmds.add(example);
        return cmds;
    }

    /**
     * 列举所有的进程
     * @return
     */
    public List<String> listProcess(){
        List<String> cmds = this.shellModal();
        cmds.add(SHELL_PS);
        return cmds;
    }

    /**
     * 列举所有模拟器
     * @return
     */
    public List<String> devices(){
        List<String> cmds = new ArrayList<>();
        cmds.add(noxPath+File.separator+ NOX_ADB);
        cmds.add(DEVICES);
        return cmds;
    }

    /**
     * 启动夜神模拟器
     * @return
     */
    public String startNox(){
        return noxPath+File.separator+"nox.exe";
    }

    /**
     * 截屏
     * @param vitualBoxFilePath
     * @return
     */
    public List<String> screenCap(String vitualBoxFilePath){
        List<String> cmds = new ArrayList<>();
        cmds.add(SHELL_SCREEN_CAP);
        cmds.add(SHELL_SCREEN_CAP_OP_DEST);
        cmds.add(vitualBoxFilePath);
        return cmds;
    }

    /**
     * 模拟滑动屏幕
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @return
     */
    public List<String> swipe(int x1,int y1,int x2,int y2){
        List<String> cmds = this.inputModal();
        cmds.add(SHELL_INPUT_SWIPE);
        cmds.add(String.valueOf(x1));
        cmds.add(String.valueOf(y1));
        cmds.add(String.valueOf(x2));
        cmds.add(String.valueOf(y2));
        return cmds;
    }

    /**
     * 模拟点击
     * @param x
     * @param y
     * @return
     */
    public List<String> click(int x,int y){
        List<String> cmds = this.inputModal();
        cmds.add(SHELL_INPUT_TAP);
        cmds.add(String.valueOf(x));
        cmds.add(String.valueOf(y));
        return cmds;
    }

    /**
     * 模拟按键
     * @param keys 按键
     * @return
     */
    public List<String> pressKey(List<String> keys){
        List<String> cmds = this.inputModal();
        cmds.add(SHELL_INPUT_KEYEVENT);
        cmds.addAll(keys);
        return cmds;
    }

    /**
     * 文本输入,不支持中文
     * @param text
     * @return
     */
    public List<String> inputText(String text){
        List<String> cmds = this.inputModal();
        cmds.add(SHELL_INPUT_TEXT);
        cmds.add(text);
        return cmds;
    }

    /**
     * 输入模式
     * @return
     */
    private List<String> inputModal() {
        List<String> cmds = this.shellModal();
        cmds.add(SHELL_INPUT);
        return cmds;
    }

    /**
     * 杀死后台所有进程
     * @return
     */
    public List<String> killAllAppProcess(){
        List<String> cmds = this.activityManager();
        cmds.add(SHELL_AM_KILL_ALL);
        return cmds;
    }

    /**
     * 杀死某个后台进程
     * @param pkgNm
     * @return
     */
    public List<String> killAppProcess(String pkgNm){
        List<String> cmds = this.activityManager();
        cmds.add(SHELL_AM_KILL);
        cmds.add(pkgNm);
        return cmds;
    }

    /**
     * 停止app
     * @param pkgName
     * @return
     */
    public List<String> stopApp(String pkgName){
        List<String> cmds = this.activityManager();
        cmds.add(SHELL_AM_STOP);
        cmds.add(pkgName);
        return cmds;
    }

    /**
     * 启动app
     * @param pkgName
     * @param activity
     * @return
     */
    public List<String> startApp(String pkgName,String activity){
        List<String> cmds = this.activityManager();
        cmds.add(SHELL_AM_START);
        cmds.add(SHELL_AM_START_OP_XSTART);
        cmds.add(pkgName+"/"+activity);
        return cmds;
    }

    /**
     * 获取某个应用的路径
     * @param pkgName
     * @return
     */
    public List<String> getAppPath(String pkgName){
        List<String> cmds = this.packageManager();
        cmds.add(SHELL_PM_PATH);
        cmds.add(pkgName);
        return cmds;
    }

    /**
     *
     * @return
     */
    public List<String> listPackageNameAndPath(){
        List<String> cmds = this.listPackageName();
        cmds.add(SHELL_PM_LIST_PACKAGES_OP_NAME_AND_PATH);
        return cmds;
    }

    /**
     * 列举所有的包名
     * @return
     */
    public List<String> listPackageName(){
        List<String> cmds=this.packageManager();
        cmds.add(SHELL_PM_LIST);
        cmds.add(SHELL_PM_LIST_PACKAGES);
        return cmds;
    }

    /**
     * 包操作指令
     * @return
     */
    private List<String> packageManager(){
        List<String> cmds = this.shellModal();
        cmds.add(SHELL_PM);
        return cmds;
    }

    /**
     * activityManager模式
     * @return
     */
    private List<String> activityManager(){
        List<String> cmds = this.shellModal();
        cmds.add(SHELL_AM);
        return cmds;
    }

    /**
     * 从模拟器拉取文件到电脑,有权限限制
     * @param virtualBoxFilePath
     * @param pcFilePath
     * @return
     */
    @Deprecated
    public List<String> pull(String virtualBoxFilePath,String pcFilePath){
        List<String> cmds = new ArrayList<>();
        cmds.add(noxPath+File.separator+virtualBoxFilePath);
        cmds.add(PULL);
        cmds.add(virtualBoxFilePath);
        cmds.add(pcFilePath);
        return cmds;
    }

    /**
     * 推送文件到模拟器,有权限限制
     * @param pcFilePath
     * @param vitualBoxFilePath
     * @return
     */
    @Deprecated
    public List<String> push(String pcFilePath,String vitualBoxFilePath){
        List<String> cmds = new ArrayList<>();
        cmds.add(noxPath+ File.separator+ NOX_ADB);
        cmds.add(PUSH);
        cmds.add(pcFilePath);
        cmds.add(vitualBoxFilePath);
        return cmds;
    }

    /**
     * 卸载app
     * @param packageName
     * @return
     */
    public List<String> uninstall(String packageName){
        List<String> cmds = new ArrayList<>();
        cmds.add(noxPath+ File.separator+ NOX_ADB);
        cmds.add(UNINSTALL);
        cmds.add(packageName);
        return cmds;
    }

    /**
     * 安装app
     * @param apkPath
     * @return
     */
    public List<String> install(String apkPath){
        List<String> cmds = new ArrayList<>();
        cmds.add(noxPath+ File.separator+ NOX_ADB);
        cmds.add(INSTALL);
        cmds.add(apkPath);
        return cmds;
    }

    /**
     * 设置mac地址
     * @param mac
     * @return
     */
    public List<String> setMac(String mac){
        List<String> cmds = this.shellModal();
        cmds.add(NOX_ADB_SHELL_SET_PROP);
        cmds.add(NOX_ADB_SHELL_SET_PROP_MAC);
        cmds.add(mac);
        return cmds;
    }

    /**
     * 设置经度
     * @param longitude
     * @return
     */
    public List<String> setLongitude(Double longitude){
        List<String> cmds = this.shellModal();
        cmds.add(NOX_ADB_SHELL_SET_PROP);
        cmds.add(NOX_ADB_SHELL_SET_PROP_LONGITUDE);
        cmds.add(longitude.toString());
        return cmds;
    }

    /**
     * 设置模拟器纬度
     * @param latitude
     * @return
     */
    public List<String> setLatitude(Double latitude){
        List<String> cmds = this.shellModal();
        cmds.add(NOX_ADB_SHELL_SET_PROP);
        cmds.add(NOX_ADB_SHELL_SET_PROP_LATITUDE);
        cmds.add(latitude.toString());
        return cmds;
    }

    /**
     * shell模式,设置模拟器属性之类的
     * @return
     */
    private List<String> shellModal(){
        List<String> cmds = new ArrayList<>();
        cmds.add(noxPath+ File.separator+ NOX_ADB);
        cmds.add(SHELL);
        return cmds;
    }

    /**
     * shell模式,清除缓存
     * @return
     */
    public List<String> clearTemp(String pckName) {
        List<String> cmds = this.shellModal();
        cmds.add(SHELL_PM_CLEAR_TEMP);
        cmds.add(pckName);
        return cmds;
    }
}

我们来测试一下。修改main方法如下:

public static void main(String...args)throws Exception{
        NoxCmdBuilder noxCmdBuilder = new NoxCmdBuilder("C:\\Program Files\\Nox\\bin");
        System.out.println(noxCmdBuilder.listPackageName());

    }

输出结果

[C:\Program Files\Nox\bin\nox_adb, shell, pm, list, packages]

直接将这个list放入ProcessBuilder中使用,就可以获得所有的包名了。我们来试一下。修改main如下

public static void main(String... args) throws Exception {
        NoxCmdBuilder noxCmdBuilder = new NoxCmdBuilder("C:\\Program Files\\Nox\\bin");
        Process start = new ProcessBuilder().command(noxCmdBuilder.listPackageName()).start();
        //此处注意的是,读取流信息的时候,有可能流对象太大,
        //不能一次性读完,导致获取的字符串顺序错乱或缺失的问题,
        //所以我们等程序执行完毕之后再去读取
        //waitFor会让线程阻塞,直至process执行完毕
        start.waitFor();
        System.out.println(readInputstream(start.getInputStream()));
    }
4人推荐
随时随地看视频
慕课网APP