1. 程式人生 > >Android Runtime.getRuntime().exec() 使用方法

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的實現程式碼:

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);
}
從上面的程式碼,我們可以看出最終呼叫的程式碼都是:exec(String[] cmdArray,String envp,File  dir)。exec(String command)相當於exec(command,null,null),exec(String[] cmdArray)相當於exec(cmdArray,null,null)。

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();
        }
    }