前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发日常:使用JNI执行任何二进制文件

Android开发日常:使用JNI执行任何二进制文件

作者头像
DioxideCN
发布2022-08-05 19:54:32
1.8K0
发布2022-08-05 19:54:32
举报
文章被收录于专栏:用户4480853的专栏

Android开发日常:使用JNI执行任何二进制文件

什么是 JNI ?

JNI是 Java Native Interface 的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。从 Java1.1 开始,JNI标准成为java平台的一部分,它允许 Java 代码和其他语言写的代码进行交互 。JNI 一开始是为了本地已编译语言,尤其是 C 和 C++ 而设计的 ,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI 标准至少要保证本地代码能工作在任何 Java 虚拟机环境。

在哪里见过?

native 关键字

  • 一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
  • 在定义一个native方法时,并不提供实现体(比较像定义一个Java Interface),因为其实现体是由非Java语言在外面实现的。
  • 主要是因为JAVA无法对操作系统底层进行操作,但是可以通过jni(java native interface)调用其他语言来实现底层的访问。
代码语言:javascript
复制
public static native test() {}

提出问题

很多时候使用 Kotlin 或 Java 开发 Android 时都离不开访问 /data/data/com.xxx.xxx/ 下的文件,受 Linux 不可控因素影响,在高版本 Android 系统中 Runtime.exec("su") 已经失效。 那么该如何使用 root 权限去执行应用包下的 二进制 文件呢?

一些前提条件

使用 native 是少不了 NDK 包的,通过 Preferences(Settings) > Appearence & Behavior > System Settings > Android SDK 中的 SDK Tools 下载 NDK 与 CMake,具体如下图:

image-1651832328162
image-1651832328162

解决方案

架构

  1. 在创建项目时使用 native c++ 模板进行创建;
  2. /src/main/ 包下会出现 cppjava 两种语言的核心包;
  3. 进入 /src/main/cpp/native-lib.cpp 中,可以看到系统已自动生成了一个 cpp 函数;

System Fork

现在使用我们二年级学过的 C++ 知识来写一个 Linux 操作让 system() 函数去执行:

代码语言:javascript
复制
#include <jni.h>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>

void* Shell()
{
    char shell[64];
    sprintf(shell,"sh /data/data/com.example.jni/start.sh");
    system(shell);
    return nullptr;
}

这样我们就创建了一个 Shell() 方法。

JNI调用

假设我们希望这个方法的名字叫做 execShell 并且提供给我们定义好的工具类 shellUtil 使用

将 Shell() 方法挂载到 JNI 实例中:

代码语言:javascript
复制
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_utils_shellUtil_execShell(JNIEnv *env, jobject thiz) {
    Shell();
}

回到需要调用的工具类(utils.shellUtil)中,写入调用:

代码语言:javascript
复制
static {
    System.loadLibrary("native_lib");
}
private static native void shell(); //使用native关键字调取

这里的 System.loadLibrary("native_lib") 的意思为:调用你 build 之后生成的 libnative_lib.so SO库。

so库在哪里

编写完 C++ native lib 之后进行 build 操作可以在文件目录 /build/intermediates/merged_native_libs/debug/out/lib 下找到对应不同操作系统的 so 库文件。 将他们复制到你的 libs(与 src 同级目录) 下后再 run 你的项目即可完成调用。

多线程

至此,已经完成了 native 库的编写与运行,你应该对 JNI 也有了一定的了解。但很多情况下我们不希望 被运行的二进制文件 阻碍 安卓主线程 这时候,需要使用到多线程对二进制文件的运行进行处理。 我们可以在 native-lib.cpp 中这样处理:

代码语言:javascript
复制
#include <jni.h>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>

void* Shell()
{
    char shell[64];
    //fork拷贝
    sprintf(shell,"sh /data/data/com.example.jni/start.sh");
    system(shell);
    return nullptr;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_utils_shellUtil_execShell(JNIEnv *env, jobject thiz) {
    //创建线程id
    pthread_t tid;
    //启动线程
    pthread_create(
        &tid,
        nullptr,
        reinterpret_cast<void *(*)(void *)>(Shell),
        nullptr
    );
}

通过 pthread 函数库进行线程处理,这样就保障了 安卓应用主线程 的线程安全,与并行的效率。

如何停止线程?

二进制文件是你写的,你问我怎么停止这个线程?

管道通信

我们在小学三年级的 Linux操作系统 课程中已经知道了 system() 命令的执行过程是 fork子进程 执行二进制,这样就带来一个问题: 我的二进制文件需要指定一个配置来启动的话就读取不到被设定为 read only 的文件夹内的资源。

如何解决?

我也不会,希望有大佬能指点江山。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Android开发日常:使用JNI执行任何二进制文件
    • 什么是 JNI ?
      • 在哪里见过?
        • native 关键字
      • 提出问题
        • 一些前提条件
          • 解决方案
            • 架构
            • System Fork
            • JNI调用
            • 多线程
            • 管道通信
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档