面试-JNI

Android NDK 从入门到精通(汇总篇)(https://blog.csdn.net/afei__/article/details/81290711)

1.JNI demo

1.1 写java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestJni {
static {
System.load("/Users/liuming/_work/java/demo/jni/libhello.so");
}

private native static String hello(String message);

public static void main(String[] args) {
String text = hello("ming");
System.out.println("Test jni-->" + text);

String text1 = hello(null);
System.out.println("Test jni-->" + text1);
}
}

1.2 生成头文件

1
2
# 自动生成native方法的头文件
java -h . TestJni.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJni */

#ifndef _Included_TestJni
#define _Included_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestJni
* Method: hello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_TestJni_hello
(JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif

1.3 实现头文件中声明的方法

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
#include "TestJni.h"
#include <string.h>
#include <stdlib.h>
#include "/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h"

JNIEXPORT jstring JNICALL Java_TestJni_hello(JNIEnv * env, jclass cls, jstring str) {
if (!str) {
return (*env)->NewStringUTF(env, "");
}

const char* s = (*env)->GetStringUTFChars(env, str, 0);
const char* head_str = "nani:";
char* dest = malloc(strlen(head_str) + strlen(s) + 1);
if (dest == NULL) {
fprintf(stderr, "%s\n", "error: malloc fail!");
return (*env)->NewStringUTF(env, "");
}

strcpy(dest, head_str);
const char* res = strcat(dest, s);

if (!dest) free(dest);
if (!s) (*env)->ReleaseStringUTFChars(env, str, s);

return (*env)->NewStringUTF(env, res);
}

1.4 编译

1
2
3
4
5
6
7
8
# -I表示在这个目录中搜索用到的头文件, 这因为没写完整的jni.h的全路径,所以要加上
gcc -c -I "/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers" hello.c

# 把hello.o打包成libhello.so
gcc -shared -o libhello.so hello.o

# .java文件编译成.class
javac TestJni.java

1.5 运行

1
java TestJni

2. 静态绑定&动态绑定

静态绑定

java_包名_类名_方法名
1
JNIEXPORT jstring JNICALL Java_TestJni_hello(JNIEnv * env, jclass cls, jstring str);

动态绑定

原理:当java层调用System.load(“xxx.so”)时,会在so中搜索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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
jstring stringFromJNI(JNIEnv *env, jclass clazz) {
return (*env)->NewStringUTF(env, "what are you fucking doing here?");
}

jint add(JNIEnv *env, jclass clazz, jint a, jint b) {
return a + b;
}

// 方法签名
// | java类型 | 类型描述符 |
// | ---- | ---- |
// | int | I |
// | long | J |
// | byte | B |
// | short | S |
// | char | C |
// | float | F |
// | double | D |
// | boolean | Z |
// | void | V |
// | 其他引用类型 | L+类全名+; |
// | 数组 | [ |
// | 方法 | (参数)返回值 |
JNIEXPORT jint JNICALL
RegisterNatives(JNIEnv *env) {
jclass clazz = (*env)->FindClass(env, "TestJni");
if (clazz == NULL) {
printf("con't find class: TestJni");
return JNI_ERR;
}
JNINativeMethod methods_TestJni[] = {
{"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},
{"add", "(II)I", (void *) add}
};
int len = sizeof(methods_TestJni) / sizeof(methods_TestJni[0]);
return (*env)->RegisterNatives(env, clazz, methods_TestJni, len);
}

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jint result = RegisterNatives(env);
printf("RegisterNatives result: %d\n", result);
return JNI_VERSION_1_6;
}

3.Android framework里,主要的jni的点