安卓开发- 在Java代码中执行shell指令

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指令的执行结果,使用步骤如下:

  1. 构建命令字符串:使用字符串拼接的方式将packageName变量嵌入到命令字符串中。例如:"ls -i /data/data | grep " + packageName"
  2. 执行命令:使用Runtime.getRuntime().exec(command)执行shell命令。
  3. 读取输出流:通过BufferedReaderprocess.getInputStream()中读取输出流。
  4. 等待进程结束:使用process.waitFor()等待命令执行完毕。
  5. 关闭资源:最后,不要忘记关闭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命令。
  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值