实现简单的NDK操作


小技巧

在这里有个小技巧,在每次创建Cpp方法的时候,都要这样写:

1
Java_top_jowanxu_jnidemo_MainActivity_stringFromJNI

当你方法多的时候,要改名的时候,就很麻烦了,所以我们可以将前面共有的提取出来,使用宏定义来实现。

首先,创建一个头文件,并且在里面定义宏:

1
2
3
4
5
6
7
// file name package-func-name
#ifndef JNIDEMO_PACKAGE_FUNC_NAME_H
#define JNIDEMO_PACKAGE_FUNC_NAME_H
#define PKFUNC(name) Java_top_jowanxu_jnidemo_##name
#endif //JNIDEMO_PACKAGE_FUNC_NAME_H

然后在我们需要使用的时候,先导入头文件,然后使用方法:

1
2
3
4
5
6
7
extern "C"
JNIEXPORT jstring JNICALL PKFUNC(MainActivity_stringFromJNI)(
JNIEnv *env,
jobject thiz) {
return env->NewStringUTF("Hello from JNI !");
}

这样就很方便了,在这里感谢李哥提供的帮助。

开始

在这里先介绍Java类型对应Jni层数据描述的关系:

基本类型:

Java类型 JNI类型 C/C++类型 大小
boolean jboolean unsigned char 无符号 8位
byte jbyte char 有符号 8位
char jchar unsigned short 无符号 16位
short jshort short 有符号 16位
int jint int 有符号 32位
long jlong long long 有符号 64位
float jfloat float 32位
double jdouble double 64位

引用类型:

Java类型 JNI类型
void void
java.lang.Class jclass
java.lang.Throwable jthrowable
java.lang.String jclass
Object jobject
type[] jtypeArray

type表示基本类型和Object。

我们来写一个最大公约数的方法,先定义一个方法:

1
external fun gcd(m: Int, n: Int): Int

然后在Cpp里面创建对应的方法:

1
2
3
4
5
6
7
8
9
10
11
12
#include "package-func-name.h"
extern "C"
JNIEXPORT jint JNICALL PKFUNC(MainActivity_gcd)(JNIEnv *env, jobject, jint m, jint n) {
jint c;
for (; m > 0;) {
c = n % m;
n = m;
m = c;
}
return n;
}

在Cpp里面定义的函数的第一个参数都是JNIEnv *env,这是什么东西呢?
JNIEnv就是JNI Environmont,JNI的环境。

再举个例子,当我们用一个引用类型参数的时候,比如一个int数组:

1
2
3
4
extern "C"
JNIEXPORT jint JNICALL PKFUNC(MainActivity_gcd)(JNIEnv *env, jobject, jintArray _data) {
}

_data其实是一个指向JVM里面对应该数组的内存的指针,你可以通过它获取数组的元素等操作,但它本身和数组无关,所以你不能对他进行操作。

只能通过JNIEnv指针来获取数组本身的指针,然后进行各种操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extern "C"
JNIEXPORT void JNICALL PKFUNC(MainActivity_getArray)(
JNIEnv *env,
jobject,
jintArray _data) {
jint *data = env->GetIntArrayElements(_data, NULL);
for (int i = 0; i < env->GetArrayLength(_data); ++i) {
LOGE("------:%d", data[i]);
}
env->ReleaseIntArrayElements(_data, data, JNI_ABORT);
// ReleaseIntArrayElements释放资源:
// JNI_ABORT表示不复制,释放资源
// JNI_OK表示复制回到JVM中,释放资源
// JNI_COMMIT表示复制,不释放资源
}

打印内容:

1
2
3
4
E/CUSTOMER_NDK_JNI: ------:1
E/CUSTOMER_NDK_JNI: ------:2
E/CUSTOMER_NDK_JNI: ------:3
E/CUSTOMER_NDK_JNI: ------:4

感谢

冰酱的JNI开发:第三章 基本类型数组操作