此时你有两种方式运行:ProcessBuilder与Runtime;两种创建方式各有千秋,至于差别详见:[Java][Android][Process] ProcessBuilder与Runtime差别
在Android中创建子进程运行命令的时候有着一定的限制:
1.JVM提供的内存有限。
2.底层缓冲区间大小有限。
3.在高并发情况下easy造成堵塞。
基于上几点在运行命令行时我们不得不慎重操作。不能随便创建。
在上一篇文章中我提到:[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
希望大家多多迁移我的项目。该类库中还带有一个自己开发的完整的日志系统,以后还会增加其它东西,比方图片处理相关(模糊等)
本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5347621.html,如需转载请自行联系原作者