Android开发当中,经常会遇到调用Linux命令的时候,而有时候我们自己在编写或者使用别人的辅助类时候,常常会遇到如下两个问题。
1、程序莫名其妙的无法响应,线程死锁无限等待。
在使用JAVA Runtime类的时候经常会遇到缓存区死锁的问题,那是因为执行命令的时候,所提供的缓冲大小有限,如果不及时处理命令往屏幕的输入,当缓冲满了,就容易出现缓冲区死锁的问题。
2、一直等待Linux的命令的返回。
这个涉及到进程之间的问题,当我们用java执行命令的时候,相当于用shell执行相应的命名,shell执行命令,会把CPU的控制权交给命令,如果此时的命令是一个常驻进程,例如mtpd,在命令没有结束之前是不会把控制权交还给shell,那么就会一直等待,直到命令处理完为止。
第一个问题好解决,及时处理命令往屏幕的输入。(及时清空缓存)
第二个问题不需要解决,shell的设计也的确如此,请遵循。
以下代码可以直接使用。
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;
import android.util.Log;
/**
* Android命令执行辅助类。建议在非主线程中执行.
* @author zoro
*
*/
public class CmdTools {
public static ExecResponseBean runCommand(String... command) {
ExecResponseBean bean = new ExecResponseBean();
bean.inPut = command;
Process proc = null;
InputStream input = null;
PrintWriter out = null;
try {
ProcessBuilder pb = new ProcessBuilder("/system/bin/sh");
pb.redirectErrorStream(true);
pb.directory(new File("/"));//设置shell的当前目录。
proc = pb.start();
//获取输入流,可以通过它获取SHELL的输出。
input = proc.getInputStream();
IOHandlerThread iHandlerThread = new IOHandlerThread(input);
iHandlerThread.start();
iHandlerThread.waitDoneInit();
//获取输出流,可以通过它向SHELL发送命令。
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
out.println("su root");//执行这一句时会弹出对话框(以下程序要求授予最高权限...),要求用户确认。
for (String cmd : command) {
out.println(cmd);
Log.v("runCommand-inPut", cmd);
}
out.println("exit");
out.flush();
closePrintWriter(out);
proc.waitFor();
byte[] msgData = iHandlerThread.getOutPut();
StringBuilder stringBuilder = new StringBuilder();
if (msgData != null) {
String msg = new String(msgData, "UTF-8");
stringBuilder.append(msg);
}
bean.outPut = stringBuilder.toString();
Log.v("runCommand-outPut", bean.outPut);
} catch (Exception e) {
System.out.println("exception:" + e);
} finally {
closeInputStream(input);
destoryProcess(proc);
}
return bean;
}
private static void destoryProcess(Process process) {
if (process != null) {
try {
process.destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void closeInputStream(InputStream inputStream) {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void closePrintWriter(PrintWriter printWriter) {
if (printWriter != null) {
try {
printWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static class ExecResponseBean {
public String outPut;
public String[] inPut;
}
private static class IOHandlerThread extends Thread {
private InputStream inputStream;
private Object lock;
private Object lock2;
private byte[] outPut;
private AtomicBoolean atomicBoolean;
private AtomicBoolean atomicBoolean2;
IOHandlerThread(InputStream inputStream) {
this.inputStream = inputStream;
lock = new Object();
lock2 = new Object();
atomicBoolean = new AtomicBoolean(false);
atomicBoolean2 = new AtomicBoolean(false);
}
void waitDoneInit() throws InterruptedException {
synchronized (lock) {
if (!atomicBoolean.get()) {
lock.wait();
}
}
}
@Override
public void run() {
byte[] data = new byte[4 * 1024];
int index = -1;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
synchronized (lock) {
atomicBoolean.set(true);
lock.notifyAll();
}
try {
while ((index = inputStream.read(data)) != -1) {
outputStream.write(data, 0, index);
}
outPut = outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
synchronized (lock2) {
atomicBoolean2.set(true);
lock2.notifyAll();
}
}
byte[] getOutPut() throws InterruptedException {
synchronized (lock2) {
if (!atomicBoolean2.get()) {
lock2.wait();
}
}
return outPut;
}
}
}