Process 创建+控制+分析 经验浅谈

移动开发 Android
无论是Android亦或者Java中或多或少需要调用底层的一些命令,执行一些参数; 此时我们需要用到Java的Process来创建一个子进程,之所以是子进程是因为此进程依赖于发起创建请求的进程,如果发起者被Kill那个子进程也将Kill。 对于Process相信使用过的朋友一定不会陌生,它具有如下特点: 1.创建简单 2.控制难 3.容易导致无法创建子进程 4.如果是多线程那么很有可能造成内存溢出 以上现象如果你只是偶尔使用一次,创建一个进程或许你什么都没有感觉到,但是如果你使用了多线程,进行了大量的创建,以上问题你都会遇到。

无论是Android亦或者Java中或多或少需要调用底层的一些命令,执行一些参数;

此时我们需要用到Java的Process来创建一个子进程,之所以是子进程是因为此进程依赖于发起创建请求的进程,如果发起者被Kill那个子进程也将Kill。

对于Process相信使用过的朋友一定不会陌生,它具有如下特点:

1.创建简单

2.控制难

3.容易导致无法创建子进程

4.如果是多线程那么很有可能造成内存溢出

以上现象如果你只是偶尔使用一次,创建一个进程或许你什么都没有感觉到,但是如果你使用了多线程,进行了大量的创建,以上问题你都会遇到。

相关:http://blog.csdn.net/qiujuer/article/details/38142273,http://blog.csdn.net/qiujuer/article/details/38086071

这两个星期一直在研究上面的问题,我要做的软件是在Android中进行TraceRoute,由于手机不可能完全Root所以不能采用JNI来发送ICMP请求的方式,最终只能使用创建进程方式进行;具体实现思路是:使用PING命令来PING百度等地址,在PING命令中加入TTL,得到每一次的IP地址,当IP地址与目标IP地址符合时退出,并且还需要单独PING一次每一跳的延迟和丢包。

单线程:PING 百度 TTL=1 =》 得到IP,PING IP 得到延迟丢包,改变TTL,进行下一次PING,直到所得到的IP与目标(百度)一样时停止。按照上面的思路一次需要创建两个子进程,一般到百度时TTL大约为12跳左右,所以就是2*12=24个子进程;如果是在单线程下简单明了,但是速度慢,整个过程大约需要1分钟左右。

多线程:同时发起3个线程进行3跳测试TTL=(1,2,3),测试完成后测试下一批数据TTL=(4,5,6),如果也是12跳的话,那么也是24个子进程,但是整体耗时将会为1/3.可见此时效率较高。

但是多线程需要考虑的是线程的同步问题,以及得到数据后的写入问题,这些赞不谈,只谈进程问题。经过我的测试假如现在测试100个网站的TraceRoute数据,在上层控制一次测试4个网站,底层实现并发3个线程,此时在一定时间内将会同时存在3*4个进程。按照平均每个网站12跳来算:12*2*100=240个子进程,需要的子线程为12*100=120个。

这个时候问题来了,假如现在程序子进程不正常了,遇到了一个一定的问题导致进程无法执行完成,此时你的现象是:一个子进程卡住,随后创建的所有子进程都卡住。假如最上层线程做了任务时间限制,那么到时间后将会尝试销毁,但是你会发现无法销毁,所持有的线程也不会销毁。但是上层以为销毁掉了,然后继续进行下一批的数据测试,此时你的线程数量会逐渐增加,如果100任务下来你的线程或许会达到3*4*100=1200如果有前期没有这样的情况那个就是一半:600个线程左右,如果后期还有任务将会继续增加但是却永远不会销毁,但是我们知道JVM的内存是有限的,所以此时将会出现内存溢出。
以上就是我遇到的问题,我***改为了等待线程完全返回后再进行下一批数据测试,此时内存溢出是解决了,但是任务却一直卡住在哪里了,永远也不走。我就在想要解决这一的问题需要解决根本上的问题才行,经过研究我发现在程序创建了子进程后JVM将会创建一个子进程管理线程:“ProcessManager”:

正常情况下该线程状态为Native,但是如果创建大量子进程后有可能会出现此线程为Monitor状态,过一段时间后所有创建子进程的线程状态也将会变为Monitor状态,然后将一直死锁,后面创建线程也是继续死锁,无法继续。

通过查看ProcessManager源码发现,其中启动了一个线程用于监听子进程状态,同时管理子进程,比如输出消息以及关闭子进程等操作,具体如下

  1. /** 
  2.  * Copyright (C) 2007 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */ 
  16.   
  17. package java.lang; 
  18.   
  19. import java.io.File; 
  20. import java.io.FileDescriptor; 
  21. import java.io.FileInputStream; 
  22. import java.io.FileOutputStream; 
  23. import java.io.IOException; 
  24. import java.io.InputStream; 
  25. import java.io.OutputStream; 
  26. import java.lang.ref.ReferenceQueue; 
  27. import java.lang.ref.WeakReference; 
  28. import java.util.HashMap; 
  29. import java.util.Map; 
  30. import java.util.Arrays; 
  31. import java.util.logging.Logger; 
  32. import java.util.logging.Level; 
  33.   
  34. /*** 
  35.  * Manages child processes. 
  36.  * 
  37.  * <p>Harmony's native implementation (for comparison purposes): 
  38.  * http://tinyurl.com/3ytwuq 
  39.  */ 
  40. final class ProcessManager { 
  41.   
  42.     /*** 
  43.      * constant communicated from native code indicating that a 
  44.      * child died, but it was unable to determine the status 
  45.      */ 
  46.     private static final int WAIT_STATUS_UNKNOWN = -1
  47.   
  48.     /*** 
  49.      * constant communicated from native code indicating that there 
  50.      * are currently no children to wait for 
  51.      */ 
  52.     private static final int WAIT_STATUS_NO_CHILDREN = -2
  53.   
  54.     /*** 
  55.      * constant communicated from native code indicating that a wait() 
  56.      * call returned -1 and set an undocumented (and hence unexpected) errno 
  57.      */ 
  58.     private static final int WAIT_STATUS_STRANGE_ERRNO = -3
  59.   
  60.     /*** 
  61.      * Initializes native static state. 
  62.      */ 
  63.     static native void staticInitialize(); 
  64.     static { 
  65.         staticInitialize(); 
  66.     } 
  67.   
  68.     /*** 
  69.      * Map from pid to Process. We keep weak references to the Process objects 
  70.      * and clean up the entries when no more external references are left. The 
  71.      * process objects themselves don't require much memory, but file 
  72.      * descriptors (associated with stdin/out/err in this case) can be 
  73.      * a scarce resource. 
  74.      */ 
  75.     private final Map<Integer, ProcessReference> processReferences 
  76.             = new HashMap<Integer, ProcessReference>(); 
  77.   
  78.     /*** Keeps track of garbage-collected Processes. */ 
  79.     private final ProcessReferenceQueue referenceQueue 
  80.             = new ProcessReferenceQueue(); 
  81.   
  82.     private ProcessManager() { 
  83.         // Spawn a thread to listen for signals from child processes. 
  84.         Thread processThread = new Thread(ProcessManager.class.getName()) { 
  85.             @Override 
  86.             public void run() { 
  87.                 watchChildren(); 
  88.             } 
  89.         }; 
  90.         processThread.setDaemon(true); 
  91.         processThread.start(); 
  92.     } 
  93.   
  94.     /*** 
  95.      * Kills the process with the given ID. 
  96.      * 
  97.      * @parm pid ID of process to kill 
  98.      */ 
  99.     private static native void kill(int pid) throws IOException; 
  100.   
  101.     /*** 
  102.      * Cleans up after garbage collected processes. Requires the lock on the 
  103.      * map. 
  104.      */ 
  105.     void cleanUp() { 
  106.         ProcessReference reference; 
  107.         while ((reference = referenceQueue.poll()) != null) { 
  108.             synchronized (processReferences) { 
  109.                 processReferences.remove(reference.processId); 
  110.             } 
  111.         } 
  112.     } 
  113.   
  114.     /*** 
  115.      * Listens for signals from processes and calls back to 
  116.      * {@link #onExit(int,int)}. 
  117.      */ 
  118.     native void watchChildren(); 
  119.   
  120.     /*** 
  121.      * Called by {@link #watchChildren()} when a child process exits. 
  122.      * 
  123.      * @param pid ID of process that exited 
  124.      * @param exitValue value the process returned upon exit 
  125.      */ 
  126.     void onExit(int pid, int exitValue) { 
  127.         ProcessReference processReference = null
  128.   
  129.         synchronized (processReferences) { 
  130.             cleanUp(); 
  131.             if (pid >= 0) { 
  132.                 processReference = processReferences.remove(pid); 
  133.             } else if (exitValue == WAIT_STATUS_NO_CHILDREN) { 
  134.                 if (processReferences.isEmpty()) { 
  135.                     /** 
  136.                      * There are no eligible children; wait for one to be 
  137.                      * added. The wait() will return due to the 
  138.                      * notifyAll() call below. 
  139.                      */ 
  140.                     try { 
  141.                         processReferences.wait(); 
  142.                     } catch (InterruptedException ex) { 
  143.                         // This should never happen. 
  144.                         throw new AssertionError("unexpected interrupt"); 
  145.                     } 
  146.                 } else { 
  147.                     /** 
  148.                      * A new child was spawned just before we entered 
  149.                      * the synchronized block. We can just fall through 
  150.                      * without doing anything special and land back in 
  151.                      * the native wait(). 
  152.                      */ 
  153.                 } 
  154.             } else { 
  155.                 // Something weird is happening; abort! 
  156.                 throw new AssertionError("unexpected wait() behavior"); 
  157.             } 
  158.         } 
  159.   
  160.         if (processReference != null) { 
  161.             ProcessImpl process = processReference.get(); 
  162.             if (process != null) { 
  163.                 process.setExitValue(exitValue); 
  164.             } 
  165.         } 
  166.     } 
  167.   
  168.     /*** 
  169.      * Executes a native process. Fills in in, out, and err and returns the 
  170.      * new process ID upon success. 
  171.      */ 
  172.     static native int exec(String[] command, String[] environment, 
  173.             String workingDirectory, FileDescriptor in, FileDescriptor out, 
  174.             FileDescriptor err, boolean redirectErrorStream) throws IOException; 
  175.   
  176.     /*** 
  177.      * Executes a process and returns an object representing it. 
  178.      */ 
  179.     Process exec(String[] taintedCommand, String[] taintedEnvironment, File workingDirectory, 
  180.             boolean redirectErrorStream) throws IOException { 
  181.         // Make sure we throw the same exceptions as the RI. 
  182.         if (taintedCommand == null) { 
  183.             throw new NullPointerException(); 
  184.         } 
  185.         if (taintedCommand.length == 0) { 
  186.             throw new IndexOutOfBoundsException(); 
  187.         } 
  188.   
  189.         // Handle security and safety by copying mutable inputs and checking them. 
  190.         String[] command = taintedCommand.clone(); 
  191.         String[] environment = taintedEnvironment != null ? taintedEnvironment.clone() : null
  192.         SecurityManager securityManager = System.getSecurityManager(); 
  193.         if (securityManager != null) { 
  194.             securityManager.checkExec(command[0]); 
  195.         } 
  196.         // Check we're not passing null Strings to the native exec. 
  197.         for (String arg : command) { 
  198.             if (arg == null) { 
  199.                 throw new NullPointerException(); 
  200.             } 
  201.         } 
  202.         // The environment is allowed to be null or empty, but no element may be null. 
  203.         if (environment != null) { 
  204.             for (String env : environment) { 
  205.                 if (env == null) { 
  206.                     throw new NullPointerException(); 
  207.                 } 
  208.             } 
  209.         } 
  210.   
  211.         FileDescriptor in = new FileDescriptor(); 
  212.         FileDescriptor out = new FileDescriptor(); 
  213.         FileDescriptor err = new FileDescriptor(); 
  214.   
  215.         String workingPath = (workingDirectory == null
  216.                 ? null 
  217.                 : workingDirectory.getPath(); 
  218.   
  219.         // Ensure onExit() doesn't access the process map before we add our 
  220.         // entry. 
  221.         synchronized (processReferences) { 
  222.             int pid; 
  223.             try { 
  224.                 pid = exec(command, environment, workingPath, in, out, err, redirectErrorStream); 
  225.             } catch (IOException e) { 
  226.                 IOException wrapper = new IOException("Error running exec()." 
  227.                         + " Command: " + Arrays.toString(command) 
  228.                         + " Working Directory: " + workingDirectory 
  229.                         + " Environment: " + Arrays.toString(environment)); 
  230.                 wrapper.initCause(e); 
  231.                 throw wrapper; 
  232.             } 
  233.             ProcessImpl process = new ProcessImpl(pid, in, out, err); 
  234.             ProcessReference processReference 
  235.                     = new ProcessReference(process, referenceQueue); 
  236.             processReferences.put(pid, processReference); 
  237.   
  238.             /** 
  239.              * This will wake up the child monitor thread in case there 
  240.              * weren't previously any children to wait on. 
  241.              */ 
  242.             processReferences.notifyAll(); 
  243.   
  244.             return process; 
  245.         } 
  246.     } 
  247.   
  248.     static class ProcessImpl extends Process { 
  249.   
  250.         /*** Process ID. */ 
  251.         final int id; 
  252.   
  253.         final InputStream errorStream; 
  254.   
  255.         /*** Reads output from process. */ 
  256.         final InputStream inputStream; 
  257.   
  258.         /*** Sends output to process. */ 
  259.         final OutputStream outputStream; 
  260.   
  261.         /*** The process's exit value. */ 
  262.         Integer exitValue = null
  263.         final Object exitValueMutex = new Object(); 
  264.   
  265.         ProcessImpl(int id, FileDescriptor in, FileDescriptor out, 
  266.                 FileDescriptor err) { 
  267.             this.id = id; 
  268.   
  269.             this.errorStream = new ProcessInputStream(err); 
  270.             this.inputStream = new ProcessInputStream(in); 
  271.             this.outputStream = new ProcessOutputStream(out); 
  272.         } 
  273.   
  274.         public void destroy() { 
  275.             try { 
  276.                 kill(this.id); 
  277.             } catch (IOException e) { 
  278.                 Logger.getLogger(Runtime.class.getName()).log(Level.FINE, 
  279.                         "Failed to destroy process " + id + ".", e); 
  280.             } 
  281.         } 
  282.   
  283.         public int exitValue() { 
  284.             synchronized (exitValueMutex) { 
  285.                 if (exitValue == null) { 
  286.                     throw new IllegalThreadStateException( 
  287.                             "Process has not yet terminated."); 
  288.                 } 
  289.   
  290.                 return exitValue; 
  291.             } 
  292.         } 
  293.   
  294.         public InputStream getErrorStream() { 
  295.             return this.errorStream; 
  296.         } 
  297.   
  298.         public InputStream getInputStream() { 
  299.             return this.inputStream; 
  300.         } 
  301.   
  302.         public OutputStream getOutputStream() { 
  303.             return this.outputStream; 
  304.         } 
  305.   
  306.         public int waitFor() throws InterruptedException { 
  307.             synchronized (exitValueMutex) { 
  308.                 while (exitValue == null) { 
  309.                     exitValueMutex.wait(); 
  310.                 } 
  311.                 return exitValue; 
  312.             } 
  313.         } 
  314.   
  315.         void setExitValue(int exitValue) { 
  316.             synchronized (exitValueMutex) { 
  317.                 this.exitValue = exitValue; 
  318.                 exitValueMutex.notifyAll(); 
  319.             } 
  320.         } 
  321.   
  322.         @Override 
  323.         public String toString() { 
  324.             return "Process[id=" + id + "]";  
  325.         } 
  326.     } 
  327.   
  328.     static class ProcessReference extends WeakReference<ProcessImpl> { 
  329.   
  330.         final int processId; 
  331.   
  332.         public ProcessReference(ProcessImpl referent, 
  333.                 ProcessReferenceQueue referenceQueue) { 
  334.             super(referent, referenceQueue); 
  335.             this.processId = referent.id; 
  336.         } 
  337.     } 
  338.   
  339.     static class ProcessReferenceQueue extends ReferenceQueue<ProcessImpl> { 
  340.   
  341.         @Override 
  342.         public ProcessReference poll() { 
  343.             // Why couldn't they get the generics right on ReferenceQueue? :( 
  344.             Object reference = super.poll(); 
  345.             return (ProcessReference) reference; 
  346.         } 
  347.     } 
  348.   
  349.     static final ProcessManager instance = new ProcessManager(); 
  350.   
  351.     /*** Gets the process manager. */ 
  352.     static ProcessManager getInstance() { 
  353.         return instance; 
  354.     } 
  355.   
  356.     /*** Automatically closes fd when collected. */ 
  357.     private static class ProcessInputStream extends FileInputStream { 
  358.   
  359.         private FileDescriptor fd; 
  360.   
  361.         private ProcessInputStream(FileDescriptor fd) { 
  362.             super(fd); 
  363.             this.fd = fd; 
  364.         } 
  365.   
  366.         @Override 
  367.         public void close() throws IOException { 
  368.             try { 
  369.                 super.close(); 
  370.             } finally { 
  371.                 synchronized (this) { 
  372.                     if (fd != null && fd.valid()) { 
  373.                         try { 
  374.                             ProcessManager.close(fd); 
  375.                         } finally { 
  376.                             fd = null
  377.                         } 
  378.                     } 
  379.                 } 
  380.             } 
  381.         } 
  382.     } 
  383.   
  384.     /*** Automatically closes fd when collected. */ 
  385.     private static class ProcessOutputStream extends FileOutputStream { 
  386.   
  387.         private FileDescriptor fd; 
  388.   
  389.         private ProcessOutputStream(FileDescriptor fd) { 
  390.             super(fd); 
  391.             this.fd = fd; 
  392.         } 
  393.   
  394.         @Override 
  395.         public void close() throws IOException { 
  396.             try { 
  397.                 super.close(); 
  398.             } finally { 
  399.                 synchronized (this) { 
  400.                     if (fd != null && fd.valid()) { 
  401.                         try { 
  402.                             ProcessManager.close(fd); 
  403.                         } finally { 
  404.                             fd = null
  405.                         } 
  406.                     } 
  407.                 } 
  408.             } 
  409.         } 
  410.     } 
  411.   
  412.     /*** Closes the given file descriptor. */ 
  413.     private static native void close(FileDescriptor fd) throws IOException; 

#p#

在其中有一个“ native void watchChildren();”方法,此方法为线程主方法,具体实现可以看看JNI,在其中回调了方法:“ void onExit(int pid, int exitValue);” 在方法中:

 

  1. void onExit(int pid, int exitValue) { 
  2.         ProcessReference processReference = null
  3.   
  4.         synchronized (processReferences) { 
  5.             cleanUp(); 
  6.             if (pid >= 0) { 
  7.                 processReference = processReferences.remove(pid); 
  8.             } else if (exitValue == WAIT_STATUS_NO_CHILDREN) { 
  9.                 if (processReferences.isEmpty()) { 
  10.                     /** 
  11.                      * There are no eligible children; wait for one to be 
  12.                      * added. The wait() will return due to the 
  13.                      * notifyAll() call below. 
  14.                      */ 
  15.                     try { 
  16.                         processReferences.wait(); 
  17.                     } catch (InterruptedException ex) { 
  18.                         // This should never happen. 
  19.                         throw new AssertionError("unexpected interrupt"); 
  20.                     } 
  21.                 } else { 
  22.                     /** 
  23.                      * A new child was spawned just before we entered 
  24.                      * the synchronized block. We can just fall through 
  25.                      * without doing anything special and land back in 
  26.                      * the native wait(). 
  27.                      */ 
  28.                 } 
  29.             } else { 
  30.                 // Something weird is happening; abort! 
  31.                 throw new AssertionError("unexpected wait() behavior"); 
  32.             } 
  33.         } 
  34.   
  35.         if (processReference != null) { 
  36.             ProcessImpl process = processReference.get(); 
  37.             if (process != null) { 
  38.                 process.setExitValue(exitValue); 
  39.             } 
  40.         } 
  41.     } 

此方法作用是删除子进程队列中子进程同时通知子进程 ProcessImpl已完成。

但是在方法:“watchChildren()”中如果出现System.in缓冲期满的情况那么进程将无法正常结束,它将一直等待缓冲区有空间存在,而缓冲区又是公共区间,如果一个出现等待那么后续子进程也将全部等待,如果缓冲区无法清空,那么所有子进程将会全部死锁掉。这就是导致子进程卡死的凶手。

知道问题关键点那么就会有人想办法解决,例如:

  1. //...读取数据... 
  2.   
  3. process.waitFor(); 
  4.   
  5. //....再次读取 

这样的方式看似很好,但是你有没有想过有些数据无法及时返回,所以在 waitfor()之前读取很有可能没有数据导致进行 waitfor()等待,这时我们可以看看源码:

  1. public int waitFor() throws InterruptedException { 
  2.             synchronized (exitValueMutex) { 
  3.                 while (exitValue == null) { 
  4.                     exitValueMutex.wait(); 
  5.                 } 
  6.                 return exitValue; 
  7.             } 
  8.         } 
  9. 1 
  10. 2 
  11. 3 
  12. 4 
  13. 5 
  14. 6 
  15.      
  16.         void setExitValue(int exitValue) { 
  17.             synchronized (exitValueMutex) { 
  18.                 this.exitValue = exitValue; 
  19.                 exitValueMutex.notifyAll(); 
  20.             } 
  21.         } 

这里可以看见假如没有退出值将会进行等待,直到通知发生,但是通知想要发生必须要靠“ ProcessManager ”线程来告诉你。但是假如在等待过程中出现了大量的数据,导致 System.IN 满了,此时“ ProcessManager ”线程很傻很傻的进入了等待状态中,也将无法进行通知,而这边也就无法往下走,无法到达第二次读取,所以第二次读取就很随机了,在大量数据下第二次读取基本上就是摆设,也就是说无法正常的执行,最终也将导致死锁。

解决办法也很简单,创建线程后我们可以创建一个线程来专门读取信息,直到“ProcessManager”线程通知结束的时候,才退出线程。

首先我们看看Process提供的“exitValue()”方法:

  1. public int exitValue() { 
  2.             synchronized (exitValueMutex) { 
  3.                 if (exitValue == null) { 
  4.                     throw new IllegalThreadStateException( 
  5.                             "Process has not yet terminated."); 
  6.                 } 
  7.   
  8.                 return exitValue; 
  9.             } 
  10.         } 

可见在” exitValue “没有值时将会抛出异常而不会阻塞,所以可以得出:” exitValue() “与” waitfor() “都可以用于判断线程是否完成,但是一个是阻塞的一个是不阻塞的方法,在线程中当然使用不阻塞的来完成我们的工作:

  1. /** 
  2.      * 实例化一个ProcessModel 
  3.      * 
  4.      * @param process Process 
  5.      */ 
  6.     private ProcessModel(Process process) { 
  7.         //init 
  8.         this.process = process; 
  9.         //get 
  10.         out = process.getOutputStream(); 
  11.         in = process.getInputStream(); 
  12.         err = process.getErrorStream(); 
  13.   
  14.         //in 
  15.         if (in != null) { 
  16.             isInReader = new InputStreamReader(in); 
  17.             bInReader = new BufferedReader(isInReader, BUFFER_LENGTH); 
  18.         } 
  19.   
  20.         sbReader = new StringBuilder(); 
  21.   
  22.         //start read thread 
  23.         readThread(); 
  24.     } 
  25.   
  26. .................... 
  27.   
  28.     //读取结果 
  29.     private void read() { 
  30.         String str; 
  31.         //read In 
  32.         try { 
  33.             while ((str = bInReader.readLine()) != null) { 
  34.                 sbReader.append(str); 
  35.                 sbReader.append(BREAK_LINE); 
  36.             } 
  37.         } catch (Exception e) { 
  38.             e.printStackTrace(); 
  39.             Logs.e(TAG, e.getMessage()); 
  40.         } 
  41.     } 
  42.   
  43.     /** 
  44.      * 启动线程进行异步读取结果 
  45.      */ 
  46.     private void readThread() { 
  47.         Thread thread = new Thread(new Runnable() { 
  48.             @Override 
  49.             public void run() { 
  50.                 // 
  51.                 while (true) { 
  52.                     try { 
  53.                         process.exitValue(); 
  54.                         //read last 
  55.                         read(); 
  56.                         break
  57.                     } catch (IllegalThreadStateException e) { 
  58.                         read(); 
  59.                     } 
  60.                     StaticFunction.sleepIgnoreInterrupt(300); 
  61.                 } 
  62.   
  63.                 //read end 
  64.                 int len; 
  65.                 if (in != null) { 
  66.                     try { 
  67.                         while ((len = in.read(BUFFER)) > 0) { 
  68.                             Logs.d(TAG, String.valueOf(len)); 
  69.                         } 
  70.                     } catch (IOException e) { 
  71.                         e.printStackTrace(); 
  72.                         Logs.e(TAG, e.getMessage()); 
  73.                     } 
  74.                 } 
  75.   
  76.                 //close 
  77.                 close(); 
  78.   
  79.                 //done 
  80.                 isDone = true
  81.             } 
  82.         }); 
  83.   
  84.         thread.setName("DroidTestAgent.Test.TestModel.ProcessModel:ReadThread"); 
  85.         thread.setDaemon(true); 
  86.         thread.start(); 
  87.   
  88.     } 

当创建进程后把进程丢进我建立的类中实例化为一个进程管理类,随后启动线程,线程执行中调用进程的” exitValue()“ ,如果异常就进入读取数据,直到不异常时再次读取一次***数据,随后退出循环,退出后还读取了一次底层的数据(这个其实可以不用要,纯属心理作用!)。***写入完成标记。其中” StaticFunction.sleepIgnoreInterrupt(300); “是我写的静态方法用于休眠等待而已,也就是 Sleep ,只不过加入了 try catch 。

当然光是读取IN流是不行的,还有Error流,这个时候就需要两个线程来完成,一个也行。不过我为了简单采用了:ProcessBuilder类创建进程并重定向了错误流到IN流中,这样简化了操作。

而使用ProcessBuilder类需要注意的是同一个ProcessBuilder实例创建子进程的时候是需要进行线程同步操作的,因为如果并发操作将会导致进程参数错误等现象发生,所以建议加上线程互斥来实现,但是不建议重复创建ProcessBuilder实例,创建那么多实例,何不把所有子进程放在一个ProcessBuilder实例里边。减少内存消耗啊,手机伤不起啊。

有必要提出的是,当线程判断结束的时候,也就是退出值(exitvalue)有值得时候此时其实在”ProcessManager“线程中已经杀掉了进程了,此时在进程中其实没有此进程了,有的也就是执行后的数据流而已。所以正常结束情况下无需自己调用”destroy()“方法,调用后将会触发异常,说没有找到此进程。

  1. public void destroy() { 
  2.             try { 
  3.                 kill(this.id); 
  4.             } catch (IOException e) { 
  5.                 Logger.getLogger(Runtime.class.getName()).log(Level.FINE, 
  6.                         "Failed to destroy process " + id + ".", e); 
  7.             } 
  8.         } 

#p#

***给大家分享我自己弄得一个类(ProcessModel),大家喜欢就直接拿去,如果有好的建议希望大家提出来:

  1. import com.droidtestagent.journal.Logs; 
  2. import com.droidtestagent.util.StaticFunction; 
  3.   
  4. import java.io.BufferedReader; 
  5. import java.io.IOException; 
  6. import java.io.InputStream; 
  7. import java.io.InputStreamReader; 
  8. import java.io.OutputStream; 
  9. import java.util.concurrent.locks.Lock; 
  10. import java.util.concurrent.locks.ReentrantLock; 
  11.   
  12. /** 
  13.  * Create By Qiujuer 
  14.  * 2014-08-05 
  15.  * <p/> 
  16.  * 执行命令行语句进程管理封装 
  17.  */ 
  18. public class ProcessModel { 
  19.     private static final String TAG = "ProcessModel"
  20.     //换行符 
  21.     private static final String BREAK_LINE; 
  22.     //错误缓冲 
  23.     private static final byte[] BUFFER; 
  24.     //缓冲区大小 
  25.     private static final int BUFFER_LENGTH; 
  26.     //创建进程时需要互斥进行 
  27.     private static final Lock lock = new ReentrantLock(); 
  28.     //ProcessBuilder 
  29.     private static final ProcessBuilder prc; 
  30.   
  31.     final private Process process; 
  32.     final private InputStream in; 
  33.     final private InputStream err; 
  34.     final private OutputStream out; 
  35.     final private StringBuilder sbReader; 
  36.   
  37.     private BufferedReader bInReader = null
  38.     private InputStreamReader isInReader = null
  39.     private boolean isDone; 
  40.   
  41.   
  42.     /** 
  43.      * 静态变量初始化 
  44.      */ 
  45.     static { 
  46.         BREAK_LINE = "\n"
  47.         BUFFER_LENGTH = 128
  48.         BUFFER = new byte[BUFFER_LENGTH]; 
  49.   
  50.         prc = new ProcessBuilder(); 
  51.     } 
  52.   
  53.   
  54.     /** 
  55.      * 实例化一个ProcessModel 
  56.      * 
  57.      * @param process Process 
  58.      */ 
  59.     private ProcessModel(Process process) { 
  60.         //init 
  61.         this.process = process; 
  62.         //get 
  63.         out = process.getOutputStream(); 
  64.         in = process.getInputStream(); 
  65.         err = process.getErrorStream(); 
  66.   
  67.         //in 
  68.         if (in != null) { 
  69.             isInReader = new InputStreamReader(in); 
  70.             bInReader = new BufferedReader(isInReader, BUFFER_LENGTH); 
  71.         } 
  72.   
  73.         sbReader = new StringBuilder(); 
  74.   
  75.         //start read thread 
  76.         readThread(); 
  77.     } 
  78.   
  79.     /** 
  80.      * 执行命令 
  81.      * 
  82.      * @param params 命令参数 eg: "/system/bin/ping", "-c", "4", "-s", "100","www.qiujuer.net" 
  83.      */ 
  84.     public static ProcessModel create(String... params) { 
  85.         Process process = null
  86.         try { 
  87.             lock.lock(); 
  88.             process = prc.command(params) 
  89.                     .redirectErrorStream(true
  90.                     .start(); 
  91.         } catch (IOException e) { 
  92.             e.printStackTrace(); 
  93.         } finally { 
  94.             //sleep 100 
  95.             StaticFunction.sleepIgnoreInterrupt(100); 
  96.             lock.unlock(); 
  97.         } 
  98.         if (process == null
  99.             return null
  100.         return new ProcessModel(process); 
  101.     } 
  102.   
  103.     /** 
  104.      * 通过Android底层实现进程关闭 
  105.      * 
  106.      * @param process 进程 
  107.      */ 
  108.     public static void kill(Process process) { 
  109.         int pid = getProcessId(process); 
  110.         if (pid != 0) { 
  111.             try { 
  112.                 android.os.Process.killProcess(pid); 
  113.             } catch (Exception e) { 
  114.                 try { 
  115.                     process.destroy(); 
  116.                 } catch (Exception ex) { 
  117.                     //ex.printStackTrace(); 
  118.                 } 
  119.             } 
  120.         } 
  121.     } 
  122.   
  123.     /** 
  124.      * 获取进程的ID 
  125.      * 
  126.      * @param process 进程 
  127.      * @return id 
  128.      */ 
  129.     public static int getProcessId(Process process) { 
  130.         String str = process.toString(); 
  131.         try { 
  132.             int i = str.indexOf("=") + 1
  133.             int j = str.indexOf("]"); 
  134.             str = str.substring(i, j); 
  135.             return Integer.parseInt(str); 
  136.         } catch (Exception e) { 
  137.             return 0
  138.         } 
  139.     } 
  140.   
  141.     //读取结果 
  142.     private void read() { 
  143.         String str; 
  144.         //read In 
  145.         try { 
  146.             while ((str = bInReader.readLine()) != null) { 
  147.                 sbReader.append(str); 
  148.                 sbReader.append(BREAK_LINE); 
  149.             } 
  150.         } catch (Exception e) { 
  151.             e.printStackTrace(); 
  152.             Logs.e(TAG, e.getMessage()); 
  153.         } 
  154.     } 
  155.   
  156.     /** 
  157.      * 启动线程进行异步读取结果 
  158.      */ 
  159.     private void readThread() { 
  160.         Thread thread = new Thread(new Runnable() { 
  161.             @Override 
  162.             public void run() { 
  163.                 //while to end 
  164.                 while (true) { 
  165.                     try { 
  166.                         process.exitValue(); 
  167.                         //read last 
  168.                         read(); 
  169.                         break
  170.                     } catch (IllegalThreadStateException e) { 
  171.                         read(); 
  172.                     } 
  173.                     StaticFunction.sleepIgnoreInterrupt(300); 
  174.                 } 
  175.   
  176.                 //read end 
  177.                 int len; 
  178.                 if (in != null) { 
  179.                     try { 
  180.                         while ((len = in.read(BUFFER)) > 0) { 
  181.                             Logs.d(TAG, String.valueOf(len)); 
  182.                         } 
  183.                     } catch (IOException e) { 
  184.                         e.printStackTrace(); 
  185.                         Logs.e(TAG, e.getMessage()); 
  186.                     } 
  187.                 } 
  188.   
  189.                 //close 
  190.                 close(); 
  191.   
  192.                 //done 
  193.                 isDone = true
  194.             } 
  195.         }); 
  196.   
  197.         thread.setName("DroidTestAgent.Test.TestModel.ProcessModel:ReadThread"); 
  198.         thread.setDaemon(true); 
  199.         thread.start(); 
  200.   
  201.     } 
  202.   
  203.     /** 
  204.      * 获取执行结果 
  205.      * 
  206.      * @return 结果 
  207.      */ 
  208.     public String getResult() { 
  209.         //waite process setValue 
  210.         try { 
  211.             process.waitFor(); 
  212.         } catch (Exception e) { 
  213.             e.printStackTrace(); 
  214.             Logs.e(TAG, e.getMessage()); 
  215.         } 
  216.   
  217.         //until startRead en 
  218.         while (true) { 
  219.             if (isDone) 
  220.                 break
  221.             StaticFunction.sleepIgnoreInterrupt(100); 
  222.         } 
  223.   
  224.         //return 
  225.         if (sbReader.length() == 0
  226.             return null
  227.         else 
  228.             return sbReader.toString(); 
  229.     } 
  230.   
  231.     /** 
  232.      * 关闭所有流 
  233.      */ 
  234.     private void close() { 
  235.         //close out 
  236.         if (out != null) { 
  237.             try { 
  238.                 out.close(); 
  239.             } catch (IOException e) { 
  240.                 e.printStackTrace(); 
  241.             } 
  242.         } 
  243.         //err 
  244.         if (err != null) { 
  245.             try { 
  246.                 err.close(); 
  247.             } catch (IOException e) { 
  248.                 e.printStackTrace(); 
  249.             } 
  250.         } 
  251.         //in 
  252.         if (in != null) { 
  253.             try { 
  254.                 in.close(); 
  255.             } catch (IOException e) { 
  256.                 e.printStackTrace(); 
  257.             } 
  258.         } 
  259.         if (isInReader != null) { 
  260.             try { 
  261.                 isInReader.close(); 
  262.             } catch (IOException e) { 
  263.                 e.printStackTrace(); 
  264.             } 
  265.         } 
  266.         if (bInReader != null) { 
  267.             try { 
  268.                 bInReader.close(); 
  269.             } catch (IOException e) { 
  270.                 e.printStackTrace(); 
  271.             } 
  272.         } 
  273.     } 
  274.   
  275.     /** 
  276.      * 销毁 
  277.      */ 
  278.     public void destroy() { 
  279.         //process 
  280.         try { 
  281.             process.destroy(); 
  282.         } catch (Exception ex) { 
  283.             kill(process); 
  284.         } 
  285.     } 

 本文链接:http://my.oschina.net/u/1377710/blog/298073

责任编辑:chenqingxiang 来源: oschina
相关推荐

2009-07-15 09:42:56

MyEclipse使用

2011-07-28 10:01:19

IOS 内存优化

2009-08-27 14:44:11

C# interfac

2011-06-08 16:22:24

白盒测试

2019-04-16 11:02:10

TCPIPLinux

2011-03-10 15:22:08

访问控制机制Java

2021-08-02 22:56:54

漏斗分析数据

2009-08-26 13:15:38

C#选择控制

2023-12-06 21:50:40

2009-08-28 12:25:58

C#静态方法

2011-09-14 09:30:27

2009-03-11 09:33:11

Lotus开发Workflow

2014-08-29 15:16:41

SDN

2021-08-19 09:17:12

IOT物联网设备分析

2023-10-10 07:05:14

G行变更系统

2009-04-16 09:08:21

Oracle开发经验

2011-07-06 10:03:30

项目管理

2009-08-24 17:24:28

C#创建XML文档

2011-03-31 09:55:59

Oracle数据库开发技术

2011-06-03 10:19:53

点赞
收藏

51CTO技术栈公众号