jni.h文件必须包含, 定义了所有jni函数声明和数据类型
JNIEnv结构包含了JNI函数表
第二个参数取决于该方法是静态还是实例调用, 是实例的话第二个参数就是实例本身, 静态的话指向类
1.签名
用于解决方法重载问题
Java方法例子:
long f(int n, String s, int[] arr);
签名: "(ILjava/lang/String;[I)J"
注意:
类描述符开头必须有L, 结尾必须有;
数组描述符开头必须有[
方法描述符 (各个参数描述符)返回值描述符, 其中参数描述符之间没有任何分隔符号
2.访问成员变量
实例成员变量
Java_method(JNIEnv *env, jobject obj)
{
jfieldID fid;
jclass cls = (*env)->GetObjectClass(env, obj);
//获得对象类
fid = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;");
//获得对象成员ID
jstring jstr = (*env)->GetObjectField(env, obj, fid);
//取得对象成员的值, 还有Get/SetIntField Get/SetFloatField
const char *str = (*env)->GetStringUTFChars(env, jstr, NULL);
...
(*env)->SetObjectField(env, obj, fid, jstr);
//设置对象成员的值
}
静态成员变量
GetStaticFieldID
Get/SetStaticIntField之类的
//注意这里第二个参数传入的是cls
3.访问成员方法
实例成员方法
jmethodID mid = GetMethodID(env, cls, "methodName", "()V");
(*env)->CallVoidMethod(env, obj, mid);
//命名规则, Call<Return Value Type>Method
静态成员方法
GetStaticMethodID
CallStatic<Return Value Type>Method
//注意这里第二个参数传入的是cls
被子类覆盖的父类方法
一样GetMethodID
CallNonvirtualVoidMethod CallNonvirtualBooleanMethod
//相当于super.f()
构造函数
构造函数的名称叫 "<init>"
result = (*env)->NewObject(env, cls, cid, param);
//cid是构造函数jmethodID, param是传入构造函数的参数
也可以把创建对象和初始化分开
obj = (*env)->AllocObject(env, cls);
(*env)->CallNonvirtualVoidMethod(env, result, cls, cid, cls);
//为啥这个是调用父类的方法??不是应该调用子类?
4.优化
用个hash表来缓存methodID, fieldID,用名字+签名做键值
安全的做法,连同类的初始化缓存Method/Field ID
5.引用
LocalRef 防止被GC回收
大概就是栈上的空间, 不能用static来缓存(如jclass就是LocalRef, 返回值为jobject/jclass等JNI函数)
native method返回(指的是返回到Java层, 从native函数返回native函数LocalRef是有效的, 跟栈上又不太一样), JVM自动释放LocalRef
用DeleteLocalRef主动释放(因为LocalRefTable是有限的防止溢出)
GlobalRef 防止被GC回收
必须是通过NewGlobalRef(env, cls)由程序员主动创建
Weak Global References 不保证不被GC
NewGlobalWeakRef
DeleteGlobalWeakRef
对当前上下文内使用的对象数量有准确的估计,建议使用PushLocalFrame(env, 10), PopLocalFrame(env, result)