Android开发-在Java中执行shell指令
这里提供了两种方法,可以在Java代码中执行shell指令:
注册 shell 为 service 服务的形式:
实际上就是将要执行的shell脚本注册为一个系统服务,然后在合适的时机,通过SystemProperties.set(“ctl.start”, serviceName)来启动服务,从而达到执行脚本文件的作用。
先在自定义的目录下创建一个shell脚本(release/customerDir/device/scripts/copy_data.sh):
#!/system/bin/sh
su
/system/bin/mount -o rw,remount /data
mkdir -p -m 777 data/temp_cache/backup/data
# 这里的xxxPkgName代表某个apk的包名
cp -rf data/data/xxxPkgName data/temp_cache/backup/data/
在init.rc中注册一个执行shell脚本的服务:
# ./system/core/rootdir/init.rc
# 注册一个服务 copy_data,执行的shell脚本是/system/bin/copy_data.sh
service copy_data /system/bin/copy_data.sh
oneshot
disabled
seclabel u:r:shell:s0
在device.mk文件中将shell文件复制到system/bin目录下:
# release/device/amlogic/t7_an400/device.mk
# 在编译时,将release/customerDir/device/scripts/copy_data.sh复制到system/bin目录下
PRODUCT_COPY_FILES += $(call find-copy-subdir-files,*,customerDir/device/scripts/,$(TARGET_COPY_OUT_SYSTEM)/bin)
最后在代码中启用这个服务:
// 使用SystemProperties.set()方法来调用copy_data这个service
SystemProperties.set("ctl.start", "copy_data");
// 注意,如果在Java代码中需要等待shell执行完了再做其他操作,那么这里需要让主线程休眠一小会(休眠时间不能设置过长,否则会报ANR),因为shell脚本是在另外一个线程执行的,主线程不会等待shell的执行结果
try {
// sleep
Thread.sleep(2000);
android.util.Log.d(TAG, "backupApplicationData: sleep ");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
使用Runtime.getRuntime().exec()方法执行shell指令
private void runCommand(String command) {
try {
Runtime.getRuntime().exec(command);
} catch (IOException e) {
android.util.Log.e(TAG, "Error executing command: " + command + "; error=", e);
}
}
// 调用:
runCommand("mkdir -p -m 777 data/data/" + pkgName);
拓展:使用Runtime.getRuntime().exec()方法还可以获取shell指令的执行结果,使用步骤如下:
- 构建命令字符串:使用字符串拼接的方式将
packageName
变量嵌入到命令字符串中。例如:"ls -i /data/data | grep " + packageName"
。 - 执行命令:使用
Runtime.getRuntime().exec(command)
执行shell命令。 - 读取输出流:通过
BufferedReader
从process.getInputStream()
中读取输出流。 - 等待进程结束:使用
process.waitFor()
等待命令执行完毕。 - 关闭资源:最后,不要忘记关闭
BufferedReader
以释放资源。
示例:
// 获取apk在data/data下的ceDataInode(应用数据索引,用于标识应用的数据目录)
public static long getDataDirectoryInode(String packageName) {
// 常规命令直接使用String即可,如 String command = "ls -la data/data " + packageName
// 这里因为有特殊字符“|”, 在String里面使用转义字符也不生效,所以才使用String[]
//String command = "ls -i /data/data \\| grep " + packageName;
String[] command = {"sh", "-c", "ls -i /data/data | grep " + packageName};
android.util.Log.d(TAG, "getDataDirectoryInode: cmd=" + command);
String inodeString = "";
long inode = 0L;
try {
// 执行指令并返回Process(执行的结果传到Process中),并注意这里要跟android.os.Process区分开
java.lang.Process process = Runtime.getRuntime().exec(command);
// 读取Process中的输入流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
// 遍历和处理数据
while ((line = bufferedReader.readLine()) != null) {
android.util.Log.d(TAG, "getDataDirectoryInode: read line=" + line);
String[] parts = line.split(" ");
if (parts.length > 1) {
inodeString = parts[0];
break;
}
}
// 等待进程结束,exitCode表示结束码
int exitCode = process.waitFor();
// 关闭资源
bufferedReader.close();
inode = Long.parseLong(inodeString);
} catch (Exception e) {
e.printStackTrace();
android.util.Log.e(TAG, "getDataDirectoryInode: error", e);
inode = 0L;
}
android.util.Log.d(TAG, "getDataDirectoryInode: inodeString=" + inodeString);
android.util.Log.d(TAG, "getDataDirectoryInode: " + inode);
return inode < 0 ? 0L : inode;
}
注意事项
- 在Android系统中,通常需要root权限来执行某些shell命令,所以需要确保你的应用有足够的权限来执行shell命令。有些命令可能因为权限或其他问题导致执行失败,这种情况下可以考虑是否使用第一种方式来实现。
- 考虑到安全性和稳定性,通常不建议在生产环境中直接执行shell命令,并且直接执行shell命令可能会降低应用的性能,在非必要的情况下尽量不要在Java代码中直接执行shell命令。