jni¶
1 javah 命令生成 C/C++头文件¶
http://luori366.github.io/JNI_doc/jni_header.html
- 示例:
- -d
:指定生成的头文件所在目录
- -o
:指定生成的文件名。(-o 或 -d 两个选项只能选择一个。)
- -jni
:(默认选项)使 javah 创建一输出文件,该文件包含 JNI 风格的本地方法函数原型。
- -classpath
:java 源文件所在目录。
- 常用格式:javah -d 目标文件路径 -jni -classpath 源文件根目录 源文件包名路径
- 示例
javah -d . -jni -classpath /home/user/android-4.4/packages/inputmethods/PinyinIME/src com.android.inputmethod.pinyin.PinyinDecoderService
#生成的头文件是这样的:com_android_inputmethod_pinyin_PinyinDecoderService.h #源文件包名路径可以查看源文件的行首,比如PinyinDecoderService中是这样的 #package com.android.inputmethod.pinyin;
2 jni 引用类型¶
2.1 局部引用¶
局部引用在native方法(c/c++方法)调用期间有效。它们在native方法返回后自动释放。每个本地引用都会耗费一定数量的Java虚拟机资源。程序员需要确保本机方法不会过度分配局部引用。尽管在本机方法返回Java后会自动释放本地引用,但过度分配本地引用可能会导致在执行本机方法期间VM耗尽内存。
- 局部引用来源:
- NewLocalRef
- FindClass、NewObject、GetObjectClass 和 NewCharArray 等
- 特点:局部引用在函数返回到 java 层时,如果 Java 层没有对返回的局部引用使用的话,局部引用就会被 JVM 自动释放。
- 原则:对象的局部引用使用完就调用
DeleteLocalRef
删除。
2.1.1 需要释放的函数¶
title: 什么类型需要释放?
1. 继承自jobject的,要么有专有函数释放如jstring,jarray,具体如ReleaseStringUTFChars、ReleaseByteArrayElements
2. 继承自jobject的其它类型(除上面所说),用DeleteLocalRef释放
NewLocalRef
FindClass、NewObject、GetObjectClass、NewCharArray
等
2.1.2 不需要释放的函数¶
title: 什么类型不用调用释放?
1. 不继承自jobject的其它类型都不用调用DeleteLocalRef,如jfieldID、jmethodID
2. JNI 基本数据类型是不需要释放的,如 jint、jlong、jchar 等等
GetFieldID
GetMethodID
2.1.3 局部引用表¶
- 局部引用是是有数量限制的,因此尽量再使用完之后释放
2.1.4 局部引用释放规则¶
- 程序员可以手动调用
DeleteLocalRef
去释放 - c层方法执行完成返回java层的时候,jvm会遍历局部引用表去释放
- 使用
PushLocalFrame/PopLocalFrame
创建/销毁局部引用栈帧的时候,在PopLocalFrame里会释放帧内创建的引用 - 如果使用
AttachCurrentThread
附加原生线程,在调用DetachCurrentThread
的时候会释放该线程创建的局部引用
2.1.5 使用局部引用帧栈¶
jint PushLocalFrame(JNIEnv *env, jint capacity);
:创建一个具体大小的局部引用帧。capacity
:设置帧大小
jobject PopLocalFrame(JNIEnv *env, jobject result);
:清除当前局部引用帧。result
:当不为 NULL 时,返回前一个帧,通常不需要,使用 NULL 即可。
- 示例
void func(JNIEnv *env) {
env->PushLocalFrame(4);
...
jstring jstr1 = env->NewStringUTF(str1.c_str());
jstring jstr2 = env->NewStringUTF(str2.c_str());
jstring jstr3 = env->NewStringUTF(str3.c_str());
jstring jstr4 = env->NewStringUTF(str4.c_str());
...
env->PopLocalFrame(NULL);
}
2.2 全局引用¶
全局引用来源:
- 调用 NewGlobalRef 基于局部引用创建
- 释放:DeleteGlobalRef
2.3 弱全局引用¶
弱全局引用来源:
- 调用 NewWeakGlobalRef 基于局部引用或全局引用创建
- 释放:DeleteWeakGlobalRef
3 数据类型¶
3.1 类型对比¶
Java Type | Native Type | Description |
---|---|---|
boolean | jboolean | unsigned 8 bits |
byte | jbyte | signed 8 bits |
char | jchar | unsigned 16 bits |
short | jshort | signed 16 bits |
int | jint | signed 32 bits |
long | jlong | signed 64 bits |
float | jfloat | 32 bits |
double | jdouble | 64 bits |
void | void | N/A |
3.2 类型签名¶
Type Signature | Java Type |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
L fully-qualified-class ; | fully-qualified-class |
[ type |
type[] |
( arg-types ) ret-type | method type |
- 示例:java 函数签名
long f (int n, String s, int[] arr);
//has the following type signature:
(ILjava/lang/String;[I)J
4 函数¶
4.1 返回对象¶
- 当返回的对象时 NULL 时,直接 return NULL 即可,java 层判断返回时是否
==null
。 - 返回局部引用对象,java 层在调用 native 方法时持有,则接管此对象的生命周期。
- 示例
JNIEXPORT jstring JNICALL Java_JNIString_strMethod(JNIEnv *env, jclass jcls, jstring jstr)
{
int i;
char str2[128];
if(jstr == NULL) // 判断传递过来的对象是否为NULL
{
printf("null\n");
return NULL; // 此外,也可以返回NULL,Java得到的将是null
}
const char *str = env->GetStringUTFChars(jstr, NULL); // must be const
// const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);
printf("%s\n", str);
strcpy(str2,str);
env->ReleaseStringUTFChars(jstr, str);
return env->NewStringUTF(str2);
}