Android Runtime.getRuntime().exec() 使用方法
Android 可以通過Runtime.getRuntime().exec()方法來執行命令或者建立程序。
1. Runtime.getRuntime().exec共有六個過載方法:
-
public Process exec(String command)
在單獨的程序中執行指定的字串命令。
-
public Process exec(String [] cmdArray)
在單獨的程序中執行指定命令和變數
-
public Process exec(String command, String [] envp)
在指定環境的獨立程序中執行指定命令和變數
-
public Process exec(String [] cmdArray, String [] envp)
在指定環境的獨立程序中執行指定的命令和變數
-
public Process exec(String command,String[] envp,File dir)
在有指定環境和工作目錄的獨立程序中執行指定的字串命令
-
public Process exec(String[] cmdarray,String[] envp,File dir)
在指定環境和工作目錄的獨立程序中執行指定的命令和變數
我們先來比較exec(String command)與exec(String[] cmdArray)的區別,其實他們是等價的,最終都會呼叫:
exec(String[] cmdarray,String[] envp,File dir),我們看看方法exec(String cmdarray,String[] envp,File dir) throws IOException的實現程式碼:
從上面的程式碼,我們可以看出最終呼叫的程式碼都是:exec(String[] cmdArray,String envp,File dir)。exec(String command)相當於exec(command,null,null),exec(String[] cmdArray)相當於exec(cmdArray,null,null)。public Process exec(String command, String[] envp, File dir) throws IOException { if (command.length() == 0) throw new IllegalArgumentException("Empty command"); StringTokenizer st = new StringTokenizer(command); String[] cmdarray = new String[st.countTokens()]; for (int i = 0; st.hasMoreTokens(); i++) { cmdarray[i] = st.nextToken(); } return exec(cmdarray, envp, dir); }
2. 引數說明:
cmdarray- 包含所呼叫命令及其引數的陣列
envp- 字串陣列,其中每個元素的環境變數的設定格式為 name=value,如果子程序應該繼承當前程序的環境,或該引數為 null
dir- 子程序的工作目錄;如果子程序應該繼承當前程序的工作目錄,則該引數為 null
3. 關於返回結果型別:Process,它有幾個方法:
(1).destroy():殺掉子程序
(2).exitValue():返回子程序的出口值,值0表示正常終止
(3).getErrorStream():獲取子程序的錯誤流
(4).getInputStream():獲取子程序的輸入流
(5).getOutputStream():獲取子程序的輸出流
(6).waitFor():導致當前執行緒等待,如有必要,一直要等到由該Process物件表示的程序已經終止。如果已終止該子程序,此方法立即返回。如果沒有終止該子程序,呼叫的執行緒將被阻塞,直到退出子程序,根據慣例, 0表示正常終止
4. 如何獲取command 最終執行的結果?(1) 執行command命令,將命令列輸出重定向到一個檔案,再讀取檔案判斷執行結果。
比如命令:javap -l xxx > output.txt , 呼叫exec(String[] cmdArray)如下:
Process p = Runtime.getRuntime().exec(new String[]{"/bin/sh","-c", "javap -l xxx > output.txt"});
(2) 也可以通過InputStream/OutputStream, 獲取命令執行的結果。
下面例子是先利用“su”提權,然後執行command,完整程式碼:
private static boolean exeCommand(String command) {
boolean ret = false;
try {
VirtualTerminal vt;
vt = new VirtualTerminal("su");
VTCommandResult r = vt.runCommand(command);
ret = r.success();
vt.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
VirtualTerminal.java
package com.test.mytest;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import android.util.Log;
public class VirtualTerminal {
private static final String TAG = "VirtualTerminal";
private final Object mReadLock = new Object();
private final Object mWriteLock = new Object();
private Process mProcess = null;
private DataOutputStream mOutputStream;
private ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream();
private ByteArrayOutputStream mErrBuffer = new ByteArrayOutputStream();
private InputReaderThread mInputReaderThread;
private InputReaderThread mErrReaderThread;
public VirtualTerminal(String shell) throws IOException, InterruptedException {
mProcess = Runtime.getRuntime().exec(shell);
mOutputStream = new DataOutputStream(mProcess.getOutputStream());
mInputReaderThread = new InputReaderThread(mProcess.getInputStream(), mInputBuffer);
mErrReaderThread = new InputReaderThread(mProcess.getErrorStream(), mErrBuffer);
Thread.sleep(50);
mInputReaderThread.start();
mErrReaderThread.start();
}
public VTCommandResult runCommand(String command) throws Exception {
synchronized (mWriteLock) {
mInputBuffer.reset();
mErrBuffer.reset();
}
// $? 表示最後執行的命令的結束程式碼(返回值)
mOutputStream.writeBytes(command + "\necho :RET=$?\n");
mOutputStream.flush();
while (true) {
synchronized (mReadLock) {
boolean doWait = false;
synchronized (mWriteLock) {
byte[] inpbyte = mInputBuffer.toByteArray();
String inp = new String(inpbyte);
doWait = !inp.contains(":RET=");
}
if (doWait) {
mReadLock.wait();
}
}
synchronized (mWriteLock) {
byte[] inpbyte = mInputBuffer.toByteArray();
byte[] errbyte = mErrBuffer.toByteArray();
String inp = new String(inpbyte);
String err = new String(errbyte);
//Please keep log statement or else it will dead loop
if (inp.contains(":RET=")) {
if (inp.contains(":RET=EOF") || err.contains(":RET=EOF")) {
Log.w(TAG, "exec:[eof]" + inp);
}
if (inp.contains(":RET=0")) {
Log.w(TAG, "exec:[ok]" + inp);
return new VTCommandResult(0, inp, err);
} else {
Log.w(TAG, "exec:[err]" + inp);
return new VTCommandResult(1, inp, err);
}
}
}
}
}
public void shutdown() {
mInputReaderThread.interrupt();
mErrReaderThread.interrupt();
mProcess.destroy();
}
/**
* A thread class helps to read/write data
*/
public class InputReaderThread extends Thread {
private InputStream mInputStream;
private ByteArrayOutputStream mByteArrayOutputStream;
public InputReaderThread(InputStream in, ByteArrayOutputStream out) {
mInputStream = in;
mByteArrayOutputStream = out;
}
@Override
public void run() {
if (mInputStream != null) {
try {
byte[] buffer = new byte[1024];
while (true) {
int read = mInputStream.read(buffer);
if (read < 0) {
synchronized(mWriteLock) {
String eof = ":RET=EOF";
mByteArrayOutputStream.write(eof.getBytes());
}
synchronized (mReadLock) {
mReadLock.notifyAll();
}
break;
} else if (read > 0) {
synchronized(mWriteLock) {
mByteArrayOutputStream.write(buffer, 0, read);
}
synchronized (mReadLock) {
mReadLock.notifyAll();
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
/**
* A result wrapper for exec()
*/
public class VTCommandResult {
public final String mStdout;
public final String mStderr;
public final Integer mExitValue;
VTCommandResult(Integer exit_value_in, String stdout_in, String stderr_in) {
mExitValue = exit_value_in;
mStdout = stdout_in;
mStderr = stderr_in;
}
VTCommandResult(Integer exit_value_in) {
this(exit_value_in, null, null);
}
public boolean success() {
return mExitValue != null && mExitValue == 0;
}
}
}
5. 如何用Worker thread 實現 exec() 的等待超時機制?
/**
* 執行一個外部命令,返回狀態, 可以設定超時時間
* @param command
* @param timeout, in milliseconds
* @return
*/
private int execCommand(final String[] commandArray, final long timeout) {
Worker worker = null;
try {
worker = new Worker(commandArray);
worker.start();
worker.join(timeout);
} catch (Throwable t) {
t.printStackTrace();
} finally {
if (worker.exit != Integer.MAX_VALUE) {
return worker.exit;
} else {
worker.interrupt();
}
}
return ErrorCodeDefine.DOROOT_FAIL;
}
/**
* 用Worker thread 可以實現超時機制
*/
private static class Worker extends Thread {
private final String[] commandArray;
private int exit = Integer.MAX_VALUE;
private Worker(String[] commandArray) {
this.commandArray = commandArray;
}
public void run() {
Process process = null;
try {
process = Runtime.getRuntime().exec(commandArray);
if (process != null) {
exit = process.waitFor();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Throwable t) {
t.printStackTrace();
} finally {
if (process != null) {
killProcess(process);
}
}
}
}
/**
* 通過Android底層實現程序關閉
*/
private static void killProcess(Process process) {
int pid = getProcessId(process.toString());
if (pid != 0) {
try {
closeAllStream(process);
android.os.Process.killProcess(pid);
} catch (Exception e) {
try {
process.destroy();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
/**
* 獲取當前程序的ID
*/
private static int getProcessId(String str) {
try {
int i = str.indexOf("=") + 1;
int j = str.indexOf("]");
String cStr = str.substring(i, j).trim();
return Integer.parseInt(cStr);
} catch (Exception e) {
return 0;
}
}
/**
* 關閉程序的所有流
*
* @param process
*/
public static void closeAllStream(Process process) {
try {
InputStream in = process.getInputStream();
if (in != null)
in.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
InputStream in = process.getErrorStream();
if (in != null)
in.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
OutputStream out = process.getOutputStream();
if (out != null)
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}