记录一些 jni 中的函数使用与静态注册相关知识。

# 可执行程序编译

首先准备 Android.mk 文件,文件内容如下所示:

1
2
3
4
5
6
7
$ LOCAL_PATH := $(call my-dir)
$ include $(CLEAR_VARS)
$ LOCAL_ARM_MODE := arm #确定编译后的指令集
$ LOCAL_MODULE := example #模块名称
$ LOCAL_SRC_FILES := JNI_example.c #c 源文件
$ LOCAL_LDLIBS += -llog #依赖库
$ include $(BUILD EXECUTABLE) #将.c 构建为可执行程序,使用 shared_library 则会生成动态链接库, 使用 static_library 会生成静态链接库

准备 Application.mk 文件,文件内代码如下所示:
1
$  APP_API := x86 armeabi-v7a

在以上文件存储处打开 cmd,执行 ndk-build 指令生成 linux 下的可执行程序。

# 相关函数

jni 中的函数可以在 /java/include 下的 jni.h 文件中查询,以下只列举了一些可能使用到的函数。

# 调用 java 层普通方法

1
$ (*CallObjectMethod) (JNIEnv*, jobject, jmethodID, ...);

前两个为默认参数,第三个参数由以下方法获取。

# 获取 java 层实例方法的值

1
$  jmethodID (*GetMethodID) (JNIEnv*, jclass, const char*, const char*);

其中,第一个 const char * 为 java 层方法名称,第二个 const char * 为 java 层方法的签名 (即方法返回类型),而 jclass 由以下方法获取:
1
$ jclass (*FindClass) (JNIEnv*, const char*)

其中,const char * 为目标所在 class 的路径 (使用 \ 代替.)。

# 获取 java 层实例字段的值

1
$ jobject (*GetObjectField) (JNIEnv*, jobject , jfieldID);

其中,jfieldID 由以下代码获取:
1
$ jfieldID (*GetFieldID) (JNIenv*, jclass, const char*, const char*);

第一个 const char * 为实例字段的名称,第二个 const char * 为实例字段签名 (即实例字段类型)。

# 设置 java 层实例字段的值

1
$ void (*SetObjectField) (JNIEnv*, jobject, jfieldID, jobject);

第二个 jobject 为 java 层实例字段设置的值。

# 静态注册

# 生成 jni 头文件

在 class 代码中声明 native 方法,之后在 cmd 窗口中切换到源码目录处,执行以下代码生成 jni 头文件:

1
$ javah -jni com.example.main.class

# 编写 c 代码

在 c 文件中 include 之前生成的头文件,之后便能在 c 文件中具体实现在头文件中声明的函数。

# 生成动态链接库

将之前的两个 mk 文件同.c 与.h 文件放在同一文件夹下,使用 ndk-build 生成动态链接库文件。

# 调用动态链接库

在 java 代码中使用以下代码调用生成的动态链接库:

1
$ System.loadLibrary ("Module")

# 总结

将以上个部分内容相结合,即可实现在 native 层调用 java 函数或获取 java 变量值。