NDK, native sdk, 通过JNI访问,JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,
通过它可以扩展很多系统底层的能力,如opencv
,openGL
,ffmpg
,以及通过将隐私代码如加密算法打包,并进行混淆,大大增加了破解难度,提供程序的安全性.
新建工程
├── androidTest
│ └── java
│ └── com
├── main
│ ├── AndroidManifest.xml
│ ├── cpp // 源代码目录
│ │ ├── CMakeLists.txt
│ │ └── native-lib.cpp
│ ├── java
│ │ └── com
│ └── res ..
└── test
└── java
└── com
cpp文件示例
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI( // Java_{packagename siplt with _}{CallerClassName}_{MethodNameInCaller}
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
在android docs中有提到除了通过dlsym
的方式查找外,还可在利用JNI_OnLoad
中显示的注册native
方法.JNI_OnLoad
是在.so
库加载之后,两者比较在JNI_OnLoad
中注册开销更小,它是懒加载的.
Note: JavaVM是Java虚拟机在JNI层的接口,它封装一些JVM操 区别: JavaVM 全局只有一个,JNIEnv
每个线程都有一个,所以千万不要夸线程直接饮用JNIEnv,可以通过
JavaVM暴露的
GetINIEnv`来获取.
CMakeList.txt示例
依赖cmake,跨平台安装构建工具,它封装了多种编译的命令,提供一种可以使用target来描述的方式来设置工程的构建行为,使得工程编译配置和依赖选项比使用原始的命令更加清晰
//cmake的最低支持版本
cmake_minimum_required(VERSION 3.10.2)
project("myapplication")
add_library( # Sets the name of the library.
myapplication //二进制库的名字,使用前通过`System.loadLibrary("myapplication")`加载,这两处名字必须一致
# Sets the library as a shared library. 库的类型, 也可以是`STATIC`
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp)
//查找并设置,log库的路径变量 log-lib
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
//将 log-lib链接到target中
target_link_libraries( # Specifies the target library.
myapplication
# Links the target library to the log library
# included in the NDK.
${log-lib})
命令非常多,但基本都围绕以下几个部分来设置
- 需要编译的源码位置描述
- 头文件搜索路径描述
- 预编译宏命令
- 依赖链接
- 编译时的link选项
- 编译时的功能选项,通常和语言特性有关,如
-std
设置
更多工程配置格式可以参考这个文档 https://cmake.org/cmake/help/latest/guide/tutorial/index.html
在主工程中使用
在使用的主工程的app
目录下添加cmake
的依赖
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.10.2'
}
}
- 在调用内`MainActivity`中声明这个方法是一个外部方法,需要加载外部库追后才能访问到
external fun stringFromJNI(): String
```kotlin
class MainActivity : AppCompatActivity() {
companion object {
// Used to load the 'myapplication' library on application startup.
init {
System.loadLibrary("myapplication1")
}
}
调用此方法
binding.sampleText.text = stringFromJNI()
so文件获取
首先解码apk,apkTool d -fs app-debug.apk
lib文件默认在apk
的lib
目录
jiodg45@jiodg45s-MacBook-Pro app-debug % cd lib
jiodg45@jiodg45s-MacBook-Pro lib % tree -L 2
.
└── x86_64
└── libmyapplication.so
实际开发需要将 so
文件和JNI
接口实现进行从新整合,封装成java简单易用的方法,整个流程并不复杂,重点还是要熟悉底层代码的开发
参考以下几篇文章
https://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/design.html#wp16696
https://developer.android.google.cn/training/articles/perf-jni
http://gityuan.com/2016/05/28/android-jni/
https://github.com/obfuscator-llvm/obfuscator