[Java][Android][Process] 暴力的服务可以解决一切,暴力的方式执行命令行语句

无论是在Java或者Android中执行命令行语句殊途同归都是创建一个子进程执行调用可执行文件执行命令,类似于Windows中的CMD一样。

此时你有两种方式执行:ProcessBuilder与Runtime;两种创建方式各有千秋,至于区别详见:[Java][Android][Process] ProcessBuilder与Runtime区别

在Android中创建子进程执行命令的时候有着一定的限制:

1.JVM提供的内存有限。

2.底层缓冲区间大小有限。

3.在高并发情况下容易造成阻塞。

基于上几点在执行命令行时我们不得不谨慎操作,不能随便创建。

在上一篇文章中我提到:[Java][Android][Process] Process 创建+控制+分析 经验浅谈 了一些我的管理方式已经对实现的分析;其归根结底为:创建一个子进程的时候同时创建一个线程用于读取输出的流信息,在返回后及时退出;图示:

通过以上的控制方式能有效的解决掉底层ProcessManager线程死掉情况(出现等待IO缓冲区情况),当此线程死掉后子进程也将进入等待且永不退出。通过这样的情况能达到执行上万条命令不出现问题。但是经过我半个月的观察发现其并不是最稳定的方式,当程序中还有很多其他IO操作如(文件读写,网络请求等)出现的时候;我执行到接近2万条命令行后出现了同样的问题。

查询后得出,虽然我们启动了线程来读取多余的流数据,但是当线程很多或者请求很多的时候线程可能来不及立即启动,而此时IO却已经满了,那么将会进入IO临界区等待,也就是说线程其实不是万能的。庆幸的是在Android中有这样一种机制:服务。

Android服务是什么?我不多说百度一下就知道!

现在来说说我的新思路:

首先咱们创建一个服务,并设置服务为独立进程:android:process=".command.CommandService"

然后把实现部分放在我们开启的服务中,使用一个类来控制服务启动以及调用服务做任务,其任务就是把上面的部分放在服务中控制实现,如图:

这样看来是不是没有区别?其实不然,区别已经来了,第一由于是独立进程的服务,所以会有独立的JVM空间,同时该进程的IO与主线程IO不是同一个(逻辑上);所以如果在主进程中操作其他的流并不影响独立进程的流。

并且有一个特别重要的情况:当独立服务进程出现死掉(IO)等待情况,这时服务中的守护进程将会自动杀掉自己;然后等待重新启动后继续执行任务。

实现代码:

public CommandServiceImpl() {
            //线程初始化
            thread = new Thread(CommandServiceImpl.class.getName()) {
                @Override
                public void run() {
                    while (thread == this && !this.isInterrupted()) {
                        if (commandExecutors != null && commandExecutors.size() > 0) {
                            lock.lock();
                            LogUtil.i(TAG, "Executors Size:" + commandExecutors.size());
                            for (CommandExecutor executor : commandExecutors) {
                                if (executor.isTimeOut())
                                    try {
                                        killSelf();
                                    } catch (RemoteException e) {
                                        e.printStackTrace();
                                    }
                                if (thread != this && this.isInterrupted())
                                    break;
                            }
                            lock.unlock();
                        }
                        try {
                            Thread.sleep(10000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            thread.setDaemon(true);
            thread.start();
        }
<span style="white-space:pre">	</span>/**
         * 杀掉自己
         *
         * @throws RemoteException
         */
        @Override
        public void killSelf() throws RemoteException {
            android.os.Process.killProcess(android.os.Process.myPid());
        }
        /**
         * 执行命令
         *
         * @param params 命令
         * @return 结果
         * @throws RemoteException
         */
        @Override
        public String command(String params) throws RemoteException {
            CommandExecutor executor = CommandExecutor.create(params);
            lock.lock();
            commandExecutors.add(executor);
            lock.unlock();
            String result = executor.getResult();
            lock.lock();
            commandExecutors.remove(executor);
            lock.unlock();
            return result;
        }

此时由于服务杀掉自己没法在内存中保存当前队列任务,那任务是否就丢弃掉呢?这肯定是不允许的,我们没法在服务中控制但是可以在控制任务的:

代码如下:

package net.qiujuer.libraries.genius.command;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;

import net.qiujuer.libraries.genius.journal.LogUtil;
import net.qiujuer.libraries.genius.utils.GlobalValue;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by Genius on 2014/8/13.
 * 命令执行Model
 */
public class CommandModel {
    private static final String TAG = CommandModel.class.getName();
    //调用服务接口
    private static ICommandInterface iService = null;
    //服务链接类,用于实例化服务接口
    private static ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iLock.lock();
            iService = ICommandInterface.Stub.asInterface(service);
            if (iService != null) {
                try {
                    iCondition.signalAll();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else
                bindService();
            iLock.unlock();
            LogUtil.i(TAG, "onServiceConnected");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iService = null;
            LogUtil.i(TAG, "onServiceDisconnected");
        }
    };
    //锁
    private static Lock iLock = new ReentrantLock();
    //等待与唤醒
    private static Condition iCondition = iLock.newCondition();

    //执行参数
    private String parameter;
    //是否取消测试
    private boolean isCancel;

    /**
     * 实例化
     *
     * @param params @param params 命令参数 eg: "/system/bin/ping", "-c", "4", "-s", "100","www.qiujuer.net"
     */
    public CommandModel(String... params) {
        //check params
        if (params == null)
            throw new NullPointerException();
        //run
        StringBuilder sb = new StringBuilder();
        for (String str : params) {
            sb.append(str);
            sb.append(" ");
        }
        this.parameter = sb.toString();
    }

    /**
     * 执行测试
     *
     * @param model ProcessModel
     * @return 结果
     */
    public static String command(CommandModel model) {
        //检测是否取消测试
        if (model.isCancel)
            return null;
        //check Service
        if (iService == null) {
            iLock.lock();
            try {
                iCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            iLock.unlock();
        }

        String result;
        try {
            result = iService.command(model.parameter);
        } catch (Exception e) {
            e.printStackTrace();
            bindService();
            result = command(model);
        }
        return result;
    }

    /**
     * 启动并绑定服务
     */
    private static void bindService() {
        Context context = GlobalValue.getContext();
        Intent intent = new Intent(context, CommandService.class);
        context.startService(intent);
        context.bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    /**
     * 取消测试
     */
    public void cancel() {
        isCancel = true;
    }

    /**
     * 静态初始化
     */
    static {
        bindService();
    }

}

其中:

    public static String command(CommandModel model) {
        //检测是否取消测试
        if (model.isCancel)
            return null;
        //check Service
        if (iService == null) {
            iLock.lock();
            try {
                iCondition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            iLock.unlock();
        }

        String result;
        try {
            result = iService.command(model.parameter);
        } catch (Exception e) {
            e.printStackTrace();
            bindService();
            result = command(model);
        }
        return result;
    }

采用回调,就是为了完成任务执行的方法!

独立进程服务接口:ICommandInterface.aidl

interface ICommandInterface {
     void killSelf();
     String command(String params);
}

命令执行者(服务中的实际功能实现):CommandExecutor.java

package net.qiujuer.libraries.genius.command;

import net.qiujuer.libraries.genius.journal.LogUtil;
import net.qiujuer.libraries.genius.utils.ToolUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by Genius on 2014/8/13.
 * 命令行执行命令
 */
class CommandExecutor {
    private static final String TAG = CommandExecutor.class.getName();
    //换行符
    private static final String BREAK_LINE;
    //错误缓冲
    private static final byte[] BUFFER;
    //缓冲区大小
    private static final int BUFFER_LENGTH;
    //创建进程时需要互斥进行
    private static final Lock LOCK = new ReentrantLock();
    //不能超过1分钟
    private static final long TIMEOUT = 60000;
    //ProcessBuilder
    private static ProcessBuilder PRC;

    final private Process process;
    final private InputStream in;
    final private InputStream err;
    final private OutputStream out;
    final private StringBuilder sbReader;

    private BufferedReader bInReader = null;
    private InputStreamReader isInReader = null;
    private boolean isDone;
    private long startTime;

    /**
     * 静态变量初始化
     */
    static {
        BREAK_LINE = "\n";
        BUFFER_LENGTH = 128;
        BUFFER = new byte[BUFFER_LENGTH];

        LOCK.lock();
        PRC = new ProcessBuilder();
        LOCK.unlock();
    }

    /**
     * 实例化一个CommandExecutor
     *
     * @param process Process
     */
    private CommandExecutor(Process process) {
        //init
        this.startTime = System.currentTimeMillis();
        this.process = process;
        //get
        out = process.getOutputStream();
        in = process.getInputStream();
        err = process.getErrorStream();

        //in
        if (in != null) {
            isInReader = new InputStreamReader(in);
            bInReader = new BufferedReader(isInReader, BUFFER_LENGTH);
        }

        sbReader = new StringBuilder();

        //start read thread
        Thread processThread = new Thread(TAG) {
            @Override
            public void run() {
                startRead();
            }
        };
        processThread.setDaemon(true);
        processThread.start();
    }

    /**
     * 执行命令
     *
     * @param param 命令参数 eg: "/system/bin/ping -c 4 -s 100 www.qiujuer.net"
     */
    protected static CommandExecutor create(String param) {
        String[] params = param.split(" ");
        CommandExecutor processModel = null;
        try {
            LOCK.lock();
            Process process = PRC.command(params)
                    .redirectErrorStream(true)
                    .start();
            processModel = new CommandExecutor(process);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //sleep 100
            ToolUtil.sleepIgnoreInterrupt(100);
            LOCK.unlock();
        }
        return processModel;
    }

    /**
     * 获取是否超时
     *
     * @return 是否超时
     */
    protected boolean isTimeOut() {
        return ((System.currentTimeMillis() - startTime) >= TIMEOUT);
    }

    //读取结果
    private void read() {
        String str;
        //read In
        try {
            while ((str = bInReader.readLine()) != null) {
                sbReader.append(str);
                sbReader.append(BREAK_LINE);
            }
        } catch (Exception e) {
            String err = e.getMessage();
            if (err != null && err.length() > 0) {
                LogUtil.e(TAG, "Read Exception:" + err);
            }
        }
    }

    /**
     * 启动线程进行异步读取结果
     */
    private void startRead() {
        //while to end
        while (true) {
            try {
                process.exitValue();
                //read last
                read();
                break;
            } catch (IllegalThreadStateException e) {
                read();
            }
            ToolUtil.sleepIgnoreInterrupt(50);
        }

        //read end
        int len;
        if (in != null) {
            try {
                while ((len = in.read(BUFFER)) > 0) {
                    LogUtil.d(TAG, "Read End:" + len);
                }
            } catch (IOException e) {
                String err = e.getMessage();
                if (err != null && err.length() > 0)
                    LogUtil.e(TAG, "Read Thread IOException:" + err);
            }
        }

        //close
        close();
        //destroy
        destroy();
        //done
        isDone = true;

    }

    /**
     * 获取执行结果
     *
     * @return 结果
     */
    protected String getResult() {
        //until startRead en
        while (!isDone) {
            ToolUtil.sleepIgnoreInterrupt(200);
        }

        //return
        if (sbReader.length() == 0)
            return null;
        else
            return sbReader.toString();
    }

    /**
     * 关闭所有流
     */
    private void close() {
        //close out
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //err
        if (err != null) {
            try {
                err.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //in
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (isInReader != null) {
            try {
                isInReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bInReader != null) {
            try {
                bInReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 销毁
     */
    private void destroy() {
        String str = process.toString();
        try {
            int i = str.indexOf("=") + 1;
            int j = str.indexOf("]");
            str = str.substring(i, j);
            int pid = Integer.parseInt(str);
            try {
                android.os.Process.killProcess(pid);
            } catch (Exception e) {
                try {
                    process.destroy();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

好了本次采用暴力方式快要结束了,对于执行命令参数可以说是比较完美的执行方式了,采用这样的方式我执行Ping测试达到10万次,并发达到500个任务同时执行没有问题,测试的设备为:魅族M9.

本次的代码我已经打包到自己的类库中,并开源到GitHub;地址:Genius-Android

希望大家多多迁移我的项目,该类库中还带有一个自己开发的完整的日志系统,以后还会加入其他东西,比如图片处理相关(模糊等)

时间: 2024-09-30 14:52:16

[Java][Android][Process] 暴力的服务可以解决一切,暴力的方式执行命令行语句的相关文章

[Android] [Java] 分享 Process 执行命令行封装类

在上一篇文章中提到,利用Java创建进程执行命令行语句创建过多后会出现无法创建进程的问题. [Android] ProcessBuilder与Runtime.getRuntime().exec分别创建进程的区别 进行多次测试后发现是因为没有正常退出进程,以及完全读取掉流数据,和关闭流导致的问题. 在多次优化后,建立如下封装类: ProcessModel.java import java.io.BufferedReader; import java.io.IOException; import j

[Java][Android][Process] Process 创建+控制+分析 经验浅谈

不管是Android亦或者Java中或多或少须要调用底层的一些命令.运行一些參数: 此时我们须要用到Java的Process来创建一个子进程.之所以是子进程是由于此进程依赖于发起创建请求的进程,假设发起者被Kill那个子进程也将Kill. 对于Process相信使用过的朋友一定不会陌生,它具有例如以下特点: 1.创建简单 2.控制难 3.easy导致无法创建子进程 4.假设是多线程那么非常有可能造成内存溢出 以上现象假设你仅仅是偶尔使用一次,创建一个进程也许你什么都没有感觉到,可是假设你使用了多

如何解决GitHub冲突&lt;二&gt;:使用命令行解决合并冲突

如何解决GitHub冲突<二>:使用命令行解决合并冲突 原文地址:https://help.github.com/desktop/guides/contributing/syncing-your-branch/ 你可以使用命令行和文本编辑器来解决"合并冲突". 合并冲突往往会发生在以下情况: (1)多个代码更改发生在同一行代码上 (2)一个提交删除了某一个文件而另一个提交尝试去编辑该文件 1.解决同行代码竞争引起的合并冲突 为了解决一个由更改同行代码引起的合并冲突,你必须决

Android 在Android代码中执行命令行

1.路径最好不要是自己拼写的路径/mnt/shell/emulated/0/wifidog.conf 最好是通过方法获取的路径,不然可能导致命令无效  (挂载点的原因) public static final String SDCARD_ROOT=Environment.getExternalStorageDirectory().getAbsolutePath(); public static final String AAA_PATH=SDCARD_ROOT+"/wifidog.conf&qu

Java或者PHP 执行命令行操作的快捷方法

有些Cli业务需要在第三方应用系统所在的服务器中运行.按照常用操作方法,这时候通常会搭建本业务需要的环境, 比如安装某些软件.配置环境变量等.有时候可能Java或PHP的版本不同,有时候本地服务器没有相关环境.这样操作,会对第三方应用系统的环境带来污染(程度可能有所不同). 按照下述方式操作,可以避免产生污染. 提取必要的DLL文件放置到某一指定文件夹下,解压缩Java或PHP后放置到相关文件夹下. 定义一个批处理文档,将上面两个文件夹加入环境变量(此时仅当前命令行进程下有效) 例如: titl

java学习总结(16.05.08)在windows下使用cmd命令行对java文件进行编译和执行

windows下利用cmd命令行可以调用jdk里的javac.exe和java.exe对java文件进行编译和执行,前提是jdk已成功安装并正确配置相关环境变量(jdk安装与环境变量的配置方法:http://blog.csdn.net/qq_32099621/article/details/51339868) 下面来说一下windows下如何使用cmd命令行来编译执行java文件 首先找到需要编译和执行的java文件 这里我要编译和执行这个java文件 按组合键win+r调出"运行",

Notepadd++命令行语句执行之--编译、运行Java

1.让Notepad++编译和运行Java,在电脑上要已经配置好了Java的开发环境 2.在Notepad++上面的选项栏中找到 Plugins--->Plugin Admin 3.在Available标签页搜索NppExec,没有安装过的,将能够搜索的出来,已经安装过的,将在Installed标签页中显示 4.安装过的插件,可以移除之后再安装,并在详情描述框给出插件的来源HomePage 5.安装完成插件之后,Plugins下将多了个NppExec,按图将相关Console勾选以便打印命令行执

java执行命令行命令

package javai; import java.io.BufferedReader; import java.io.InputStreamReader; public class IOTest { public static void main(String[] args) { try { Process pro = Runtime.getRuntime().exec("ping www.baidu.com"); String line; BufferedReader buf =

使用java对执行命令行 或 执行bat文件

public class Hellotianhao { public static void main(String[] args) throws Exception{ System.out.println("hello tianhao"); Runtime.getRuntime().exec("cmd /k mkdir d:\\xutianhao"); } } 运行结果是在d盘新建了一个名为xutianhao的文件夹 java执行bat文件  bat文件书写注意在