NDK开发总结

  1. 1 背景
  2. 2 JNI简介
  3. 3 静态注册和动态注册
    1. 3.1 静态注册
    2. 3.2 动态注册
    3. 3.3 区别
  4. 4 参考文献

1 背景

经过NDK开发的入门学习后,顿感自己啥也不是😅😅😅,还是好好学习吧

2 JNI简介

JNI(Java Native Interface)是指Java原生接口,它定义了Android从受管理代码(使用Java或Kotlin编程语言编写)编译的字节码与原生代码(使用C/C++编写)互动的方式。同时,这个特性使我们可以复用以前用C/C++写的大量代码。JNI不依赖于供应商,支持从动态共享库加载代码,虽然有时较为繁琐,但效率尚可。

JNI开发流程主要分为以下6步:

  1. 编写声明了native方法的Java类
  2. 将Java源代码编译成class字节码文件
  3. 用javah -jni命令生成.h头文件(javah是jdk自带的一个命令,-jni参数表示将class中用native声明的函数生成jni规则的函数)
  4. 用本地代码实现.h头文件中的函数
  5. 将本地代码编译成动态库(Windows:*.dll,linux/Unix:*.so,Mac OS X:*.jnilib)
  6. 拷贝动态库至java.library.path本地库搜索目录下,并运行Java程序

图1 JNI开发流程

3 静态注册和动态注册

为什么需要注册?其实就是给Java的native函数找到底层C/C++实现的函数指针

3.1 静态注册

通过包名、类名一致来确认,Java有一个命令javah,专门生成某一个JAVA文件所有的native函数的头文件(h文件),静态方法注册JNI有哪些缺点?

  • 必须遵循某些规则;
  • 名字过长;
  • 多个class文件需Javah多遍;
  • 运行时去找效率不高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**************静态方法**********************/
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_calc_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
/*************************************************************/
JNIEXPORT void JNICALL Java_com_example_jni_1demo_MainActivity_javaToC(JNIEnv *env, jobject obj)
{
// 获取 类
jclass fdClass = env->FindClass("com/example/jni_demo/MainActivity");
// 获取 普通方法id
jmethodID _jmethodID = env->GetMethodID(fdClass, "_method", "()V");
// 获取 静态方法id
jmethodID _staticjmethodID = env->GetStaticMethodID(fdClass, "_staticMethod", "()V");
// 调用 java中 的 普通方法
env->CallVoidMethod(obj, _jmethodID);
// 调用 java中 的 静态方法
env->CallStaticVoidMethod(fdClass, _staticjmethodID);
}

【注】在so文件中,静态注册方法的参数个数=Java层方法参数个数+其余参数个数(JNIEnv,jobject)

3.2 动态注册

在JNI层实现的,JAVA层不需要关心,因为在system.load函数执行时就会去调用JNI_OnLoad,有就注册,没就不注册。流程清晰可控,效率更高,安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/************************* 动态注册 native 方法 ********************************/ 
JNINativeMethod nativeMethod[] = { // 方法数组映射
// 定义数组,用于绑定 java方法 和 C方法的 关系
{"addMethod", "(FF)F", (void*)my_add}, // java中方法名,方法签名,C++中方法名
{"subMethod", "(FF)F", (void*)my_sub},
{"mulMethod", "(FF)F", (void*)my_mul},
{"divMethod", "(FF)F", (void*)my_div}
};

/************************* 实现 JNI_OnLoad 动态注册方法 *******************************/
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env;
if(vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
{
return JNI_ERR;
}
// 获取 java native 方法对应的 类
jclass fdClass = env->FindClass("com/example/calc/MainActivity");
// 注册 java 层 native 方法
jint retVal = env->RegisterNatives(fdClass, nativeMethod, sizeof(nativeMethod)/sizeof(nativeMethod[0]));
if(retVal != JNI_OK)
{
// 注册失败返回 -1
return JNI_ERR;
}
return JNI_VERSION_1_6; //必须返回一个版本号
}

【注】在so文件中,动态注册通常先搜索JNI_OnLoad,然后找RegisterNatives的第三个参数,接着找方法数组映射关系

3.3 区别

静态注册:

  • 绑定java方法和C/C++方法的方式之一;
  • java层操作和c层操作以及整体编译:
    1. 定义被native修饰的方法
    2. 根据java代码生成.h头文件(javah -jni 类的包名路径)
    3. 编写C/C++代码,导入.h头文件,实现我们.h头文件中方法
    4. 编写(配置)两个mk文件:application.mk/android.mk
    5. 通过ndk-build生成so文件
    6. java代码中加载so文件(system.loadlibrary)
    7. 补充:获取so库的名称
      1-libs文件下去头去尾(头:lib;尾:.so);
      2-在android.mk直接复制模块名称;

动态注册

  • 作用:绑定java方法和C/C++方法的方式之一
  • 流程:
    1. java中定义native的方法
    2. 创建C++代码代码,导入头文件,编写(配置)两个mk文件:application.mk/android.mk
    3. JNInativeMethod:绑定java方法和C/C++的方法
    4. registerNatives(4个参数):注册java层相应的类以及方法
    5. 使用jni.h中JNI_onload进行判断:注册是否成功(JIN_onload:系统调用,相当于java中的psvm:public static void main)
    6. ndk-build生成so
    7. java代码中加载so文件(system.loadlibrary)

静态注册是用到时加载动态注册一开始就加载好了,这个可以从DVM的源代码看出来。

图2 JavaVM和JNIEnv的体系结构

虚拟机相关的变量中有两个非常重要的量JavaVM和JNIEnv,两者本质上都是指向函数表的二级指针:

  • JavaVM:是指进程虚拟机环境,每个进程有且只有一个JavaVM实例
  • JNIEnv:是指线程上下文环境,每个线程有且只有一个JNIEnv实例

4 参考文献

[1]丰生强. Android软件安全权威指南[M]. 电子工业出版社, 2019.
[2]https://developer.android.google.cn/training/articles/perf-jni#java
[3]https://blog.csdn.net/xyang81/article/details/41777471
[4]https://cloud.tencent.com/developer/article/1356493


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,可以邮件至 xingshuaikun@163.com。

×

喜欢就点赞,疼爱就打赏