跳转至

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 局部引用释放规则

  1. 程序员可以手动调用 DeleteLocalRef 去释放
  2. c层方法执行完成返回java层的时候,jvm会遍历局部引用表去释放
  3. 使用PushLocalFrame/PopLocalFrame创建/销毁局部引用栈帧的时候,在PopLocalFrame里会释放帧内创建的引用
  4. 如果使用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 返回对象

  1. 当返回的对象时 NULL 时,直接 return NULL 即可,java 层判断返回时是否 ==null
  2. 返回局部引用对象,java 层在调用 native 方法时持有,则接管此对象的生命周期。
  3. 示例
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);
}