小技巧
在这里有个小技巧,在每次创建Cpp
方法的时候,都要这样写:1
Java_top_jowanxu_jnidemo_MainActivity_stringFromJNI
当你方法多的时候,要改名的时候,就很麻烦了,所以我们可以将前面共有的提取出来,使用宏定义来实现。
首先,创建一个头文件,并且在里面定义宏:1
2
3
4
5
6
7// file name package-func-name
然后在我们需要使用的时候,先导入头文件,然后使用方法: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
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
4extern "C"
JNIEXPORT jint JNICALL PKFUNC(MainActivity_gcd)(JNIEnv *env, jobject, jintArray _data) {
}
_data
其实是一个指向JVM
里面对应该数组的内存的指针,你可以通过它获取数组的元素等操作,但它本身和数组无关,所以你不能对他进行操作。
只能通过JNIEnv
指针来获取数组本身的指针,然后进行各种操作。
1 |
|
打印内容:1
2
3
4E/CUSTOMER_NDK_JNI: ------:1
E/CUSTOMER_NDK_JNI: ------:2
E/CUSTOMER_NDK_JNI: ------:3
E/CUSTOMER_NDK_JNI: ------:4