本文小編為大家詳細介紹“Process實例分析”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Process實例分析”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
通過查看ProcessManager源碼發現,其中啟動了一個線程用于監聽子進程狀態,同時管理子進程,比如輸出消息以及關閉子進程等操作,具體如下
/** * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.lang; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; import java.util.Arrays; import java.util.logging.Logger; import java.util.logging.Level; /*** * Manages child processes. * * <p>Harmony's native implementation (for comparison purposes): * http://tinyurl.com/3ytwuq */ final class ProcessManager { /*** * constant communicated from native code indicating that a * child died, but it was unable to determine the status */ private static final int WAIT_STATUS_UNKNOWN = -1; /*** * constant communicated from native code indicating that there * are currently no children to wait for */ private static final int WAIT_STATUS_NO_CHILDREN = -2; /*** * constant communicated from native code indicating that a wait() * call returned -1 and set an undocumented (and hence unexpected) errno */ private static final int WAIT_STATUS_STRANGE_ERRNO = -3; /*** * Initializes native static state. */ static native void staticInitialize(); static { staticInitialize(); } /*** * Map from pid to Process. We keep weak references to the Process objects * and clean up the entries when no more external references are left. The * process objects themselves don't require much memory, but file * descriptors (associated with stdin/out/err in this case) can be * a scarce resource. */ private final Map<Integer, ProcessReference> processReferences = new HashMap<Integer, ProcessReference>(); /*** Keeps track of garbage-collected Processes. */ private final ProcessReferenceQueue referenceQueue = new ProcessReferenceQueue(); private ProcessManager() { // Spawn a thread to listen for signals from child processes. Thread processThread = new Thread(ProcessManager.class.getName()) { @Override public void run() { watchChildren(); } }; processThread.setDaemon(true); processThread.start(); } /*** * Kills the process with the given ID. * * @parm pid ID of process to kill */ private static native void kill(int pid) throws IOException; /*** * Cleans up after garbage collected processes. Requires the lock on the * map. */ void cleanUp() { ProcessReference reference; while ((reference = referenceQueue.poll()) != null) { synchronized (processReferences) { processReferences.remove(reference.processId); } } } /*** * Listens for signals from processes and calls back to * {@link #onExit(int,int)}. */ native void watchChildren(); /*** * Called by {@link #watchChildren()} when a child process exits. * * @param pid ID of process that exited * @param exitValue value the process returned upon exit */ void onExit(int pid, int exitValue) { ProcessReference processReference = null; synchronized (processReferences) { cleanUp(); if (pid >= 0) { processReference = processReferences.remove(pid); } else if (exitValue == WAIT_STATUS_NO_CHILDREN) { if (processReferences.isEmpty()) { /** * There are no eligible children; wait for one to be * added. The wait() will return due to the * notifyAll() call below. */ try { processReferences.wait(); } catch (InterruptedException ex) { // This should never happen. throw new AssertionError("unexpected interrupt"); } } else { /** * A new child was spawned just before we entered * the synchronized block. We can just fall through * without doing anything special and land back in * the native wait(). */ } } else { // Something weird is happening; abort! throw new AssertionError("unexpected wait() behavior"); } } if (processReference != null) { ProcessImpl process = processReference.get(); if (process != null) { process.setExitValue(exitValue); } } } /*** * Executes a native process. Fills in in, out, and err and returns the * new process ID upon success. */ static native int exec(String[] command, String[] environment, String workingDirectory, FileDescriptor in, FileDescriptor out, FileDescriptor err, boolean redirectErrorStream) throws IOException; /*** * Executes a process and returns an object representing it. */ Process exec(String[] taintedCommand, String[] taintedEnvironment, File workingDirectory, boolean redirectErrorStream) throws IOException { // Make sure we throw the same exceptions as the RI. if (taintedCommand == null) { throw new NullPointerException(); } if (taintedCommand.length == 0) { throw new IndexOutOfBoundsException(); } // Handle security and safety by copying mutable inputs and checking them. String[] command = taintedCommand.clone(); String[] environment = taintedEnvironment != null ? taintedEnvironment.clone() : null; SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { securityManager.checkExec(command[0]); } // Check we're not passing null Strings to the native exec. for (String arg : command) { if (arg == null) { throw new NullPointerException(); } } // The environment is allowed to be null or empty, but no element may be null. if (environment != null) { for (String env : environment) { if (env == null) { throw new NullPointerException(); } } } FileDescriptor in = new FileDescriptor(); FileDescriptor out = new FileDescriptor(); FileDescriptor err = new FileDescriptor(); String workingPath = (workingDirectory == null) ? null : workingDirectory.getPath(); // Ensure onExit() doesn't access the process map before we add our // entry. synchronized (processReferences) { int pid; try { pid = exec(command, environment, workingPath, in, out, err, redirectErrorStream); } catch (IOException e) { IOException wrapper = new IOException("Error running exec()." + " Command: " + Arrays.toString(command) + " Working Directory: " + workingDirectory + " Environment: " + Arrays.toString(environment)); wrapper.initCause(e); throw wrapper; } ProcessImpl process = new ProcessImpl(pid, in, out, err); ProcessReference processReference = new ProcessReference(process, referenceQueue); processReferences.put(pid, processReference); /** * This will wake up the child monitor thread in case there * weren't previously any children to wait on. */ processReferences.notifyAll(); return process; } } static class ProcessImpl extends Process { /*** Process ID. */ final int id; final InputStream errorStream; /*** Reads output from process. */ final InputStream inputStream; /*** Sends output to process. */ final OutputStream outputStream; /*** The process's exit value. */ Integer exitValue = null; final Object exitValueMutex = new Object(); ProcessImpl(int id, FileDescriptor in, FileDescriptor out, FileDescriptor err) { this.id = id; this.errorStream = new ProcessInputStream(err); this.inputStream = new ProcessInputStream(in); this.outputStream = new ProcessOutputStream(out); } public void destroy() { try { kill(this.id); } catch (IOException e) { Logger.getLogger(Runtime.class.getName()).log(Level.FINE, "Failed to destroy process " + id + ".", e); } } public int exitValue() { synchronized (exitValueMutex) { if (exitValue == null) { throw new IllegalThreadStateException( "Process has not yet terminated."); } return exitValue; } } public InputStream getErrorStream() { return this.errorStream; } public InputStream getInputStream() { return this.inputStream; } public OutputStream getOutputStream() { return this.outputStream; } public int waitFor() throws InterruptedException { synchronized (exitValueMutex) { while (exitValue == null) { exitValueMutex.wait(); } return exitValue; } } void setExitValue(int exitValue) { synchronized (exitValueMutex) { this.exitValue = exitValue; exitValueMutex.notifyAll(); } } @Override public String toString() { return "Process[id=" + id + "]"; } } static class ProcessReference extends WeakReference<ProcessImpl> { final int processId; public ProcessReference(ProcessImpl referent, ProcessReferenceQueue referenceQueue) { super(referent, referenceQueue); this.processId = referent.id; } } static class ProcessReferenceQueue extends ReferenceQueue<ProcessImpl> { @Override public ProcessReference poll() { // Why couldn't they get the generics right on ReferenceQueue? :( Object reference = super.poll(); return (ProcessReference) reference; } } static final ProcessManager instance = new ProcessManager(); /*** Gets the process manager. */ static ProcessManager getInstance() { return instance; } /*** Automatically closes fd when collected. */ private static class ProcessInputStream extends FileInputStream { private FileDescriptor fd; private ProcessInputStream(FileDescriptor fd) { super(fd); this.fd = fd; } @Override public void close() throws IOException { try { super.close(); } finally { synchronized (this) { if (fd != null && fd.valid()) { try { ProcessManager.close(fd); } finally { fd = null; } } } } } } /*** Automatically closes fd when collected. */ private static class ProcessOutputStream extends FileOutputStream { private FileDescriptor fd; private ProcessOutputStream(FileDescriptor fd) { super(fd); this.fd = fd; } @Override public void close() throws IOException { try { super.close(); } finally { synchronized (this) { if (fd != null && fd.valid()) { try { ProcessManager.close(fd); } finally { fd = null; } } } } } } /*** Closes the given file descriptor. */ private static native void close(FileDescriptor fd) throws IOException; }
在其中有一個“ native void watchChildren();”方法,此方法為線程主方法,具體實現可以看看JNI,在其中回調了方法:“ void onExit(int pid, int exitValue);” 在方法中:
void onExit(int pid, int exitValue) { ProcessReference processReference = null; synchronized (processReferences) { cleanUp(); if (pid >= 0) { processReference = processReferences.remove(pid); } else if (exitValue == WAIT_STATUS_NO_CHILDREN) { if (processReferences.isEmpty()) { /** * There are no eligible children; wait for one to be * added. The wait() will return due to the * notifyAll() call below. */ try { processReferences.wait(); } catch (InterruptedException ex) { // This should never happen. throw new AssertionError("unexpected interrupt"); } } else { /** * A new child was spawned just before we entered * the synchronized block. We can just fall through * without doing anything special and land back in * the native wait(). */ } } else { // Something weird is happening; abort! throw new AssertionError("unexpected wait() behavior"); } } if (processReference != null) { ProcessImpl process = processReference.get(); if (process != null) { process.setExitValue(exitValue); } } }
此方法作用是刪除子進程隊列中子進程同時通知子進程 ProcessImpl已完成。
但是在方法:“watchChildren()”中如果出現System.in緩沖期滿的情況那么進程將無法正常結束,它將一直等待緩沖區有空間存在,而緩沖區又是公共區間,如果一個出現等待那么后續子進程也將全部等待,如果緩沖區無法清空,那么所有子進程將會全部死鎖掉。這就是導致子進程卡死的兇手。
知道問題關鍵點那么就會有人想辦法解決,例如:
//...讀取數據... process.waitFor(); //....再次讀取
這樣的方式看似很好,但是你有沒有想過有些數據無法及時返回,所以在 waitfor()之前讀取很有可能沒有數據導致進行 waitfor()等待,這時我們可以看看源碼:
public int waitFor() throws InterruptedException { synchronized (exitValueMutex) { while (exitValue == null) { exitValueMutex.wait(); } return exitValue; } } ? 1 2 3 4 5 6 void setExitValue(int exitValue) { synchronized (exitValueMutex) { this.exitValue = exitValue; exitValueMutex.notifyAll(); } }
這里可以看見假如沒有退出值將會進行等待,直到通知發生,但是通知想要發生必須要靠“ ProcessManager ”線程來告訴你。但是假如在等待過程中出現了大量的數據,導致 System.IN 滿了,此時“ ProcessManager ”線程很傻很傻的進入了等待狀態中,也將無法進行通知,而這邊也就無法往下走,無法到達第二次讀取,所以第二次讀取就很隨機了,在大量數據下第二次讀取基本上就是擺設,也就是說無法正常的執行,最終也將導致死鎖。
解決辦法也很簡單,創建線程后我們可以創建一個線程來專門讀取信息,直到“ProcessManager”線程通知結束的時候,才退出線程。
首先我們看看Process提供的“exitValue()”方法:
public int exitValue() { synchronized (exitValueMutex) { if (exitValue == null) { throw new IllegalThreadStateException( "Process has not yet terminated."); } return exitValue; } }
可見在” exitValue “沒有值時將會拋出異常而不會阻塞,所以可以得出:” exitValue() “與” waitfor() “都可以用于判斷線程是否完成,但是一個是阻塞的一個是不阻塞的方法,在線程中當然使用不阻塞的來完成我們的工作:
/** * 實例化一個ProcessModel * * @param process Process */ private ProcessModel(Process process) { //init 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 readThread(); } .................... //讀取結果 private void read() { String str; //read In try { while ((str = bInReader.readLine()) != null) { sbReader.append(str); sbReader.append(BREAK_LINE); } } catch (Exception e) { e.printStackTrace(); Logs.e(TAG, e.getMessage()); } } /** * 啟動線程進行異步讀取結果 */ private void readThread() { Thread thread = new Thread(new Runnable() { @Override public void run() { // while (true) { try { process.exitValue(); //read last read(); break; } catch (IllegalThreadStateException e) { read(); } StaticFunction.sleepIgnoreInterrupt(300); } //read end int len; if (in != null) { try { while ((len = in.read(BUFFER)) > 0) { Logs.d(TAG, String.valueOf(len)); } } catch (IOException e) { e.printStackTrace(); Logs.e(TAG, e.getMessage()); } } //close close(); //done isDone = true; } }); thread.setName("DroidTestAgent.Test.TestModel.ProcessModel:ReadThread"); thread.setDaemon(true); thread.start(); }
當創建進程后把進程丟進我建立的類中實例化為一個進程管理類,隨后啟動線程,線程執行中調用進程的” exitValue()“ ,如果異常就進入讀取數據,直到不異常時再次讀取一次***數據,隨后退出循環,退出后還讀取了一次底層的數據(這個其實可以不用要,純屬心理作用?。?。***寫入完成標記。其中” StaticFunction.sleepIgnoreInterrupt(300); “是我寫的靜態方法用于休眠等待而已,也就是 Sleep ,只不過加入了 try catch 。
當然光是讀取IN流是不行的,還有Error流,這個時候就需要兩個線程來完成,一個也行。不過我為了簡單采用了:ProcessBuilder類創建進程并重定向了錯誤流到IN流中,這樣簡化了操作。
而使用ProcessBuilder類需要注意的是同一個ProcessBuilder實例創建子進程的時候是需要進行線程同步操作的,因為如果并發操作將會導致進程參數錯誤等現象發生,所以建議加上線程互斥來實現,但是不建議重復創建ProcessBuilder實例,創建那么多實例,何不把所有子進程放在一個ProcessBuilder實例里邊。減少內存消耗啊,手機傷不起啊。
有必要提出的是,當線程判斷結束的時候,也就是退出值(exitvalue)有值得時候此時其實在”ProcessManager“線程中已經殺掉了進程了,此時在進程中其實沒有此進程了,有的也就是執行后的數據流而已。所以正常結束情況下無需自己調用”destroy()“方法,調用后將會觸發異常,說沒有找到此進程。
public void destroy() { try { kill(this.id); } catch (IOException e) { Logger.getLogger(Runtime.class.getName()).log(Level.FINE, "Failed to destroy process " + id + ".", e); } }
ProcessModel:
import com.droidtestagent.journal.Logs; import com.droidtestagent.util.StaticFunction; 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; /** * Create By Qiujuer * 2014-08-05 * <p/> * 執行命令行語句進程管理封裝 */ public class ProcessModel { private static final String TAG = "ProcessModel"; //換行符 private static final String BREAK_LINE; //錯誤緩沖 private static final byte[] BUFFER; //緩沖區大小 private static final int BUFFER_LENGTH; //創建進程時需要互斥進行 private static final Lock lock = new ReentrantLock(); //ProcessBuilder private static final 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; /** * 靜態變量初始化 */ static { BREAK_LINE = "\n"; BUFFER_LENGTH = 128; BUFFER = new byte[BUFFER_LENGTH]; prc = new ProcessBuilder(); } /** * 實例化一個ProcessModel * * @param process Process */ private ProcessModel(Process process) { //init 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 readThread(); } /** * 執行命令 * * @param params 命令參數 eg: "/system/bin/ping", "-c", "4", "-s", "100","www.qiujuer.net" */ public static ProcessModel create(String... params) { Process process = null; try { lock.lock(); process = prc.command(params) .redirectErrorStream(true) .start(); } catch (IOException e) { e.printStackTrace(); } finally { //sleep 100 StaticFunction.sleepIgnoreInterrupt(100); lock.unlock(); } if (process == null) return null; return new ProcessModel(process); } /** * 通過Android底層實現進程關閉 * * @param process 進程 */ public static void kill(Process process) { int pid = getProcessId(process); if (pid != 0) { try { android.os.Process.killProcess(pid); } catch (Exception e) { try { process.destroy(); } catch (Exception ex) { //ex.printStackTrace(); } } } } /** * 獲取進程的ID * * @param process 進程 * @return id */ public static int getProcessId(Process process) { String str = process.toString(); try { int i = str.indexOf("=") + 1; int j = str.indexOf("]"); str = str.substring(i, j); return Integer.parseInt(str); } catch (Exception e) { return 0; } } //讀取結果 private void read() { String str; //read In try { while ((str = bInReader.readLine()) != null) { sbReader.append(str); sbReader.append(BREAK_LINE); } } catch (Exception e) { e.printStackTrace(); Logs.e(TAG, e.getMessage()); } } /** * 啟動線程進行異步讀取結果 */ private void readThread() { Thread thread = new Thread(new Runnable() { @Override public void run() { //while to end while (true) { try { process.exitValue(); //read last read(); break; } catch (IllegalThreadStateException e) { read(); } StaticFunction.sleepIgnoreInterrupt(300); } //read end int len; if (in != null) { try { while ((len = in.read(BUFFER)) > 0) { Logs.d(TAG, String.valueOf(len)); } } catch (IOException e) { e.printStackTrace(); Logs.e(TAG, e.getMessage()); } } //close close(); //done isDone = true; } }); thread.setName("DroidTestAgent.Test.TestModel.ProcessModel:ReadThread"); thread.setDaemon(true); thread.start(); } /** * 獲取執行結果 * * @return 結果 */ public String getResult() { //waite process setValue try { process.waitFor(); } catch (Exception e) { e.printStackTrace(); Logs.e(TAG, e.getMessage()); } //until startRead en while (true) { if (isDone) break; StaticFunction.sleepIgnoreInterrupt(100); } //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(); } } } /** * 銷毀 */ public void destroy() { //process try { process.destroy(); } catch (Exception ex) { kill(process); } } }
讀到這里,這篇“Process實例分析”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。