无论是在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()) {@Overridepublic 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*/@Overridepublic void killSelf() throws RemoteException {android.os.Process.killProcess(android.os.Process.myPid());}
        /*** 执行命令** @param params 命令* @return 结果* @throws RemoteException*/@Overridepublic 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() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {iLock.lock();iService = ICommandInterface.Stub.asInterface(service);if (iService != null) {try {iCondition.signalAll();} catch (Exception e) {e.printStackTrace();}} elsebindService();iLock.unlock();LogUtil.i(TAG, "onServiceConnected");}@Overridepublic 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 paramsif (params == null)throw new NullPointerException();//runStringBuilder 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 Serviceif (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 Serviceif (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;//ProcessBuilderprivate 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) {//initthis.startTime = System.currentTimeMillis();this.process = process;//getout = process.getOutputStream();in = process.getInputStream();err = process.getErrorStream();//inif (in != null) {isInReader = new InputStreamReader(in);bInReader = new BufferedReader(isInReader, BUFFER_LENGTH);}sbReader = new StringBuilder();//start read threadThread processThread = new Thread(TAG) {@Overridepublic 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 100ToolUtil.sleepIgnoreInterrupt(100);LOCK.unlock();}return processModel;}/*** 获取是否超时** @return 是否超时*/protected boolean isTimeOut() {return ((System.currentTimeMillis() - startTime) >= TIMEOUT);}//读取结果private void read() {String str;//read Intry {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 endwhile (true) {try {process.exitValue();//read lastread();break;} catch (IllegalThreadStateException e) {read();}ToolUtil.sleepIgnoreInterrupt(50);}//read endint 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);}}//closeclose();//destroydestroy();//doneisDone = true;}/*** 获取执行结果** @return 结果*/protected String getResult() {//until startRead enwhile (!isDone) {ToolUtil.sleepIgnoreInterrupt(200);}//returnif (sbReader.length() == 0)return null;elsereturn sbReader.toString();}/*** 关闭所有流*/private void close() {//close outif (out != null) {try {out.close();} catch (IOException e) {e.printStackTrace();}}//errif (err != null) {try {err.close();} catch (IOException e) {e.printStackTrace();}}//inif (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
希望大家多多迁移我的项目,该类库中还带有一个自己开发的完整的日志系统,以后还会加入其他东西,比如图片处理相关(模糊等)

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

  1. 【Java】Java编写Telnet客户端,连接到Windows的Telnet服务器,执行命令和批处理脚本

    Java编写Telnet客户端,连接到Windows的Telnet服务器,执行命令和批处理脚本,同时解决了中文乱码的问题. 源代码和Jar包在这里下载:http://download.csdn.net ...

  2. Java虚拟机学习(8):查看JVM参数及值的命令行工具

    查看JVM各个参数值方式 1. HotSpot vm中的各个globals.hpp文件  查看jvm初始的默认值及参数 globals.hpp globals_extension.hpp c1_glo ...

  3. 进程android.process.acore已意外停止解决方法

    今天我的手机也出现了这样的情况,我的手机是中兴V889D,老手机了,刷了N遍机了,但是刷机包自带的通讯功能不是很好用,我还是习惯于用来电通,在安装了来电通之后,我把rom自带的短信.通讯录.联系人神马 ...

  4. android执行命令行取得结果,Android调用shell脚本并取得输出

    Android调用shell脚本并获得输出 前段时间做的HLS流媒体服务器可以正常工作了,但是它的启动需要在PC机命令行中进行或者在Android下载个Terminal IDE软件,在Android上 ...

  5. java执行数据库命令行_java程序执行命令行,解锁数据库表

    有些表锁的时间长或其他原因,在plsql中不能解锁,只能用命令行解锁. 有些功能跨平台系统的交互偶尔会锁表,就需要自动解锁. 下面是解锁的代码: package com.lg.BreakOracleU ...

  6. 用java代码执行命令行并获取返回结果

    public class CmdUtil {public static void main(String[] args) {System.out.println(CmdUtil.exec(new St ...

  7. UE4 Windows环境下游戏打包基础教程(ios, windows, android)(UFE方式以及命令行方式)

    文章目录 环境 配置 一些会用到的路径 UFE打包 Windows Android IOS 命令行打包 注意事项 CMD指令 参数介绍 值得一看的参考 环境 Windows10 虚幻4.23.1 配置 ...

  8. android cmd 右键菜单不见了,为Windows右键菜单提供打开命令行选项

    下面是编程之家 jb51.cc 通过网络收集整理的代码片段. 编程之家小编现在分享给大家,也给大家做个参考. :: 更新说明 :: :: 版本:V1.0 :: 日期:2012-07-31 :: 说明: ...

  9. Java数组学习笔记(遍历、排序、多维数组、命令行参数)

    文章目录 数组的遍历和快速打印 冒泡排序和快速排序 数组和字符串排序的区别 多维数组建立和输出 命令行向main()传递参数 数组的遍历和快速打印 一.数组的遍历 数组的遍历有两种方法,一种是使用fo ...

  10. MySQL下载与安装、mysql服务启动与停止、mysql使用cmd命令行登录、SQLyog下载与安装,sqlyog登录与操作mysql

    文章目录 1.MySQL下载与安装 1.1 下载地址 1.2 打开官网,点击DOWNLOADS 1.3 点击 MySQL Community Server 1.4 在General Availabil ...

最新文章

  1. POJ-1041 John's trip
  2. Kibana——数据图形化制作
  3. Leetcode题库 15.三数之和_1(双指针 C实现)
  4. 绥化a货翡翠,拉萨a货翡翠
  5. 我的硬盘居然被win10安装工具_安装win10无法识别硬盘,你需要这样做!网友:涨知识了...
  6. Windows 7 下安装 Oracle 数据库和 PL/SQL Developer
  7. 卸载利器IObit Uninstaller Portable v9.4.0.20绿化版
  8. dom 无法找到 body节点问题
  9. Effective JavaScript Item 37 认识this的隐式指向
  10. cxf 本地wsdl_CXF wsdl2java 详解及常见问题
  11. 软媒魔方 6.0.5 正式绿色版
  12. 大前端 HTML基础
  13. OpenCV-细化算法(thinning algorithm)描绘出轮廓的中心线
  14. C#下支付宝新版异步回调数据处理及校验(需支付宝提供的AopSdk)
  15. Unity-tweak-tool插件
  16. 云计算厂商怎么打造自己的生态网络
  17. Linux内核网络学习
  18. Python兼职半月赚了5570元:边学习边赚钱真的很爽!
  19. ng2-ace-editor 在 angular 12+ 高版本中无法经过 Ivy编译问题
  20. 细细品味C#——重构的艺术

热门文章

  1. MongoDB 在windows shell环境下的基本操作和命令的使用示例(二)
  2. 【Codeforces Round #291 (Div. 2) D】R2D2 and Droid Army【线段树+二分】
  3. 刷题记录 kuangbin带你飞专题六:最小生成树
  4. 怎么修改mysql主键(id)的值为自增
  5. SQL Server 日期+4位流水号
  6. 用于查询当前数据库中所有表格的记录条数的脚本
  7. CDC相关知识点总结
  8. 关于304缓存 (转沫鱼的前端世界)
  9. struts2学到屎挫死-深入Struts2(2)--Action
  10. 在GlassFish中应用Hibernate