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

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

什么是 JNI?

JNI,即 Java Native Interface 的缩写,通过使用 Java 本地接口编写程序,可以确保代码在不同平台上方便移植。从 Java 1.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)调用其他语言来实现底层的访问。

提出问题

很多时候使用 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,具体如下图:

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

解决方案架构

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

System Fork

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

#include #include #include #include #include 

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

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

JNI调用

宣小二 宣小二

宣小二:媒体发稿平台,自媒体发稿平台,短视频矩阵发布平台,基于AI驱动的企业自助式投放平台。

宣小二 21 查看详情 宣小二

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

extern "C"JNIEXPORT void JNICALLJava_com_example_jni_utils_shellUtil_execShell(JNIEnv env, jobject thiz) {Shell();}

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

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 你的项目即可完成调用。

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

多线程

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

#include 

include

include

include

include

include

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

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

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

如何停止线程?管道通信

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

如何解决?我也不会,希望有大佬能指点江山。

以上就是Android开发日常:使用JNI执行任何二进制文件的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/468917.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月8日 06:45:43
下一篇 2025年11月8日 06:49:11

相关推荐

  • Golang如何在MacOS配置Zsh开发环境

    安装Go并配置Zsh环境变量:下载对应芯片版本的Go安装包,安装至默认路径/usr/local/go;2. 编辑~/.zshrc文件,添加GOROOT、GOPATH及PATH,使终端识别go命令;3. 执行source ~/.zshrc生效配置,通过go version和go env验证安装与环境变…

    2025年12月16日
    000
  • Golang如何实现微服务日志收集

    答案:Go微服务通过统一结构化日志输出、标准输出写入、集中采集与上下文追踪实现高效日志收集。1. 使用zap等库输出JSON格式日志;2. 日志写入stdout/stderr,由容器运行时捕获;3. 部署Promtail、Fluent Bit等Agent将日志发送至Loki或Elasticsearc…

    2025年12月16日
    000
  • Golang如何配置远程调试功能

    答案:Go语言可通过Delve工具实现远程调试。首先在远程服务器使用dlv exec启动程序并监听指定端口,确保防火墙放行该端口;然后在本地通过VS Code或dlv connect命令连接调试会话,即可进行断点设置与变量查看等操作。 Go语言支持通过dlv(Delve)工具实现远程调试,适合在服务…

    2025年12月16日
    000
  • 如何在Golang中优化容器化应用启动时间

    通过编译优化、镜像精简、初始化控制和平台协同,可显著缩短Golang容器应用冷启动时间。具体包括:使用CGO_ENABLED=0、-ldflags=”-s -w”和-trimpath减小二进制体积;采用多阶段Docker构建,基于distroless等最小基础镜像减少运行时开…

    2025年12月16日
    000
  • Golang如何实现文件批量处理工具

    答案:使用Golang实现文件批量处理工具需遍历目录、筛选文件并并发处理。通过filepath.Walk递归遍历,按扩展名等条件过滤文件;利用goroutine和sync.WaitGroup实现并发控制,避免资源耗尽;处理逻辑可抽象为函数变量,支持替换文本、重命名、编码转换等操作;结合channel…

    2025年12月16日
    000
  • 如何在Golang中获取函数参数个数

    使用reflect.TypeOf和NumIn方法可获取函数参数个数,示例中example函数有3个参数,输出为3;对于可变参数函数如variadicExample,NumIn返回2,IsVariadic返回true,表明其为可变参数函数,需注意反射仅适用于函数类型且存在性能开销。 在Golang中,…

    2025年12月16日
    000
  • Golang如何实现Web表单上传文件

    前端表单需设置enctype=”multipart/form-data”以正确提交文件数据。2. 后端使用net/http和mime/multipart解析表单,调用r.ParseMultipartForm(32 在Go语言中实现Web表单上传文件,主要依赖标准库 net/h…

    2025年12月16日
    000
  • PHP与Go基于Unix域套接字的进程间通信:解决连接管理与读取阻塞问题

    本文深入探讨了如何利用unix域套接字实现php与go程序间的进程间通信(ipc)。通过一个具体的案例,我们分析了php客户端在读取go服务器响应时可能遇到的无限等待问题,并提供了核心解决方案——在go服务器端正确关闭客户端连接。文章涵盖了go服务器和php客户端的实现细节、关键代码示例以及连接管理…

    2025年12月16日
    000
  • Go语言中向已关闭的Tar归档文件追加内容的实现方法

    在go语言中,直接向已关闭的`tar`归档文件追加内容并非直观操作。由于`tar`文件规范要求归档以1024字节的零填充记录作为结束标记,`tar.writer.close()`会写入这些标记。本文将详细介绍如何通过以读写模式重新打开文件,并巧妙地将文件指针回溯1024字节,从而覆盖结束标记,实现向…

    2025年12月16日
    000
  • 如何在Golang中实现协程同步

    Golang中协程同步主要通过sync.WaitGroup、sync.Mutex、channel和sync.Cond实现。使用sync.WaitGroup可等待一组协程完成,适用于批量任务场景;通过Add(n)设置计数,每个协程结束调用Done(),主线程调用Wait()阻塞直至所有任务结束。当多个…

    2025年12月16日
    000
  • Go语言中带有互斥锁的结构体如何安全地实现JSON序列化

    本文探讨了在go语言中,当结构体包含`sync.rwmutex`并自定义`marshaljson`方法时,如何避免因内部递归调用`json.marshal`而导致的无限循环问题。核心解决方案是利用类型别名来创建一个不带自定义序列化方法的副本,从而在确保数据并发安全的同时,实现结构体的正确json编码…

    2025年12月16日
    000
  • GNU Make中动态目标生成与多维迭代构建策略

    本文探讨了在gnu make中实现跨平台多架构动态构建的策略。针对`:=`无法在目标定义时动态评估自动变量的问题,我们引入了`foreach`、`eval`和`define`的组合用法,通过定义模板并动态生成目标及其配方,有效解决了需要迭代不同操作系统和架构组合进行构建的场景,从而避免了手动枚举所有…

    2025年12月16日
    000
  • Go语言并发执行外部命令:构建高效协程池的最佳实践

    本文详细探讨了在Go语言中高效、可控地并发执行大量外部命令的策略。针对简单`go`关键字导致的问题和传统`WaitGroup`批处理的局限性,文章提出并详细阐述了基于工作池(Worker Pool)模式的解决方案,通过结合通道(channel)进行任务分发和`sync.WaitGroup`进行任务完…

    2025年12月16日
    000
  • Go Datastore Put 操作中的“无效实体类型”错误解析

    本文深入探讨了在使用 Go 语言的 Google Cloud Datastore 客户端库执行 `Put` 操作时,可能遇到的“datastore: invalid entity type”错误。核心问题在于 `datastore.Put` 函数期望接收一个指向结构体的指针,而非结构体值本身。文章通…

    2025年12月16日
    000
  • 使用 Go 的 net/http 包服务 PHP 文件

    本文介绍了如何使用 Go 的 net/http 包来服务 PHP 文件。由于 Go 的 FCGI 包的局限性,推荐使用 CGI 或通过反向代理(如 Nginx)将 Go 程序和 PHP FCGI 程序结合起来。后者效率更高,并能利用反向代理的诸多优势,例如 keep-alive 超时等,从而提高整体…

    2025年12月16日
    000
  • 获取 Go 中 reflect.Interface 类型值的正确方法

    本文旨在阐述在 Go 语言中,如何正确获取类型为 `reflect.Interface` 的值。由于 Go 语言的反射机制对接口类型的特殊处理,直接使用 `reflect.TypeOf` 获取接口的类型可能会得到非预期的结果。本文将介绍一种通过复合类型间接获取 `reflect.Interface`…

    2025年12月16日
    000
  • 如何在Golang中判断变量是否为数组或切片

    答案:通过reflect.TypeOf(v).Kind()判断变量是否为数组或切片,若值为reflect.Array或reflect.Slice则对应为数组或切片类型,使用时需先判空避免panic。 在Golang中判断一个变量是否为数组或切片,可以通过反射(reflect包)来实现。Go语言的类型…

    2025年12月16日
    000
  • Go语言中高效反转32位二进制数的位操作技巧

    本文将深入探讨在go语言中,如何利用位操作(bit manipulation)技术高效地反转一个32位无符号整数(uint32)的二进制位序。通过一系列精心设计的位移和掩码操作,我们能够实现极快的二进制反转,这对于性能敏感的场景,如位数组处理或加密算法,具有重要意义。 在底层编程、加密算法或竞技编程…

    2025年12月16日
    000
  • 如何在Golang中使用semantic import version

    Go要求v2+模块路径必须包含版本号,以避免API不兼容问题。正确做法是修改go.mod中module为含/v2的路径,发布时打v2.0.0等标签,导入时使用带/v2的完整路径,否则会导致版本解析错误和编译失败。 在 Golang 中使用语义导入版本(Semantic Import Versioni…

    2025年12月16日
    000
  • Go Goroutine深度解析:与协程的本质差异与调度机制

    go语言的goroutine是一种轻量级并发原语,常与传统协程(coroutine)混淆。本文旨在明确区分goroutine与协程的核心差异:协程通过显式指令控制挂起与恢复,而goroutine则在特定运行时事件(如i/o、通道操作)发生时隐式地让出控制权。文章还将探讨goroutine的调度机制,…

    2025年12月16日
    000

发表回复

登录后才能评论
关注微信