JNI 常用API
利用JNIEnv自变量,程序员可访问一系列函数。这些函数可划分为下述类别:
■获取版本信息
■进行类和对象操作
■控制对Java对象的全局和局部引用
■访问实例字段和静态字段
■调用实例方法和静态方法
■执行字串和数组操作
■产生和控制Java异常
获取版本信息
jint GetVersion(JNIEnv *env);
返回值为jint
类型,在c/c++中jint为32位数,其中高16位是主版本号,低16位是从版本号
In JDK/JRE 1.1, GetVersion()
returns0x00010001
.
In JDK/JRE 1.2, GetVersion()
returns0x00010002
.
In JDK/JRE 1.4, GetVersion()
returns0x00010004
.
jclass FindClass(JNIEnv *env, const char*name);
在CLASSPATH
环境变量中
搜索目录和zip文件中具有指定名称的类。
参数:
name
: a fully-qualified classname /
”隔开
SINCE JDK/JRE 1.2:
产生和控制Java异常
jint Throw(JNIEnv *env, jthrowable obj);
抛出异常
参数:
jthrowable
java.lang.Throwable
包中对象.
jint ThrowNew(JNIEnv *env, jclass clazz,
const char *message);
通过消息构造一个异常类,其中参数clazz
为java.lang.Throwable
包中的子类
jthrowable ExceptionOccurred(JNIEnv *env);
测试JVM是否有异常发生
SINCE JDK/JRE 1.2:
local reference 和 global reference。
Java 和JNI代码之间函数调用时,简单类型,也就是内置类型,比如 int, char 等是值传递(pass byvalue),而其它 Java 对象都是引用传递(pass by reference),这些对象引用由 JVM传给JNI代码,每个都有其生命周期。
JNI 函数参数中 jobject 或者它的子类,其参数都是 local reference。Local reference只在这个 JNI函数中有效,JNI函数返回后,引用的对象就被释放,它的生命周期就结束了。若要留着日后使用,则需根据这个 localreference 创建 global reference。Global reference 不会被系统自动释放,它仅当被程序明确调用DeleteGlobalReference 时才被回收。(JNI多线程机制)
jobject NewGlobalRef(JNIEnv *env, jobject obj);
创建一个新的全局的引用,只能使用DeleteGlobalRef
()函数销毁这个全局引用
参数:
Obj
一个本地引用或者全局引用
void DeleteGlobalRef(JNIEnv *env, jobjectglobalRef);
销毁全局引用
参数:
globalRef
使用
NewGlobalRef
()函数生成的全局引用
销毁本地的引用
注意:
JDK/JRE 1.1 提供了上面的 DeleteLocalRef函数,使程序员可以手动删除本地引用。例如,如果本机代码遍历可能很大的对象或数组,并使用每个迭代中的一个元素,好的做法是下一次迭代中创建一个新的本地引用之前删除本地元素的引用。
JDK/JRE 1.2和更高版本为本地引用生存周期管理提供了一套额外的函数。他们是下面列出的四种函数。
jint EnsureLocalCapacity(JNIEnv *env, jintcapacity);
通知JVM 您将使用超过 16 个本地引用。这将允许 JVM 优化对该本机代码的本地引用的处理
jint PushLocalFrame(JNIEnv *env, jintcapacity);
jobject PopLocalFrame(JNIEnv *env, jobjectresult);
先调用PushLocalFrame,然后创建局部引用,并对其进行处理,最后调用PushLocalFrame释放局部引用,这时Java虚拟机也可以对其指向的对象进行垃圾回收。可以用C语言的栈来理解这对JNIAPI,调用PushLocalFrame之后Native代码创建的所有局部引用全部入栈,当调用PopLocalFrame之后,入栈的局部引用除了需要返回的局部引用(PushLocalFrame和PopLocalFrame这对函数可以返回一个局部引用给外部)之外,全部出栈,Java虚拟机这时可以释放他们指向的对象。具体的用法可以参考手册。这两个函数使JNI的局部引用由于和C语言的局部变量用法类似,所以强烈推荐使用
jobject NewLocalRef(JNIEnv *env, jobjectref);
1、Java虚拟机默认为Native引用分配的局部引用数量是有限的,大部分的Java虚拟机实现默认分配16个局部引用。当然Java虚拟机也提供API(PushLocalFrame,EnsureLocalCapacity)让你申请更多的局部引用数量(Java虚拟机不保证你一定能申请到)。有限的资源当然要省着点用,否则将会被Java虚拟机无情抛弃(程序崩溃)。JNI编程中,实现Native代码时强烈建议调用PushLocalFrame,EnsureLocalCapacity来确保Java虚拟机为你准备好了局部变量空间。
2、如果你实现的Native函数是工具函数,会被频繁的调用。如果你在Native函数中没有显示删除局部引用,那么每次调用该函数Java虚拟机都会创建一个新的局部引用,造成局部引用过多。尤其是该函数在Native代码中被频繁调用,代码的控制权没有交还给Java虚拟机,所以Java虚拟机根本没有机会释放这些局部变量。退一步讲,就算该函数直接返回给Java虚拟机,也不能保证没有问题,我们不能假设Native函数返回Java虚拟机之后,Java虚拟机马上就会回收Native函数中创建的局部引用,依赖于Java虚拟机实现。所以我们在实现Native函数时一定要记着删除不必要的局部引用,否则你的程序就有潜在的风险,不知道什么时候就会爆发。
3、如果你Native函数根本就不返回。比如消息循环函数——死循环等待消息,处理消息。如果你不显示删除局部引用,很快将会造成Java虚拟机的局部引用内存溢出。
全局的弱引用是一种特殊的全局引用,与普通的全局引用不同全局的弱引用允许Java对象进行垃圾回收,当垃圾收集器运行时,它将释放对象,如果引用的对象只使用弱引用
jweak NewWeakGlobalRef(JNIEnv *env, jobjectobj);
void DeleteWeakGlobalRef(JNIEnv *env, jweakobj);
SINCE JDK/JRE 1.2:
Object Operations
jobject AllocObject(JNIEnv *env, jclass clazz);
分配一个新的 Java 对象,而不调用任何对象的构造函数,仅仅是内存创建
jobject NewObject(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);
jobject NewObjectA(JNIEnv*env, jclass clazz,
jmethodID methodID, jvalue *args);
args
: an array of arguments to the constructor.
jobject NewObjectV(JNIEnv*env, jclass clazz,
jmethodID methodID, va_list args);
args
: a va_list of arguments to theconstructor.
分配一个新的java对象,调用指定的构造函数,构造函数使用methodID
指定
jclass GetObjectClass(JNIEnv *env, jobjectobj);
根据类的引用返回类的类型
jboolean IsInstanceOf(JNIEnv *env, jobjectobj,
jclass clazz);
测试类的引用类型,是返回
JNI_TRUE
否则返回JNI_FALSE
jboolean IsSameObject(JNIEnv *env, jobjectref1,
jobject ref2);
测试两个引用类型是否指向同一个空间,是返回
JNI_TRUE
否则返回JNI_FALSE
Accessing Fields of Objects
jfieldID GetFieldID(JNIEnv *env, jclassclazz,
const char *name, const char *sig);
GetFieldID
是得到
java
类中的参数
ID
,只能调用类中声明为
public
的属性,
jfieldID
为
Get<type>Field andSet<type>Field 函数族使用
参数:
Name
属性在
java
类中的名字
Sig
类型签名
Get<type>FieldRoutines
NativeTypeGet<type>Field(JNIEnv*env, jobject obj,
jfieldID fieldID);
表 3-3 |
Get<type>FieldRoutine Name | Native Type |
| jobject |
GetBooleanField() | jboolean |
GetByteField() | jbyte |
| jchar |
| jshort |
GetIntField() | jint |
| jlong |
| jfloat |
GetDoubleField() | jdouble |
获取java对象属性的值
Set<type>FieldRoutines
void
Set<type>Field(JNIEnv*env, jobject obj, jfieldID fieldID,
NativeType value);
表 3-4
Set<type>FieldRoutine Name | Native Type |
Set | jobject |
SetBooleanField() | jboolean |
SetByteField() | jbyte |
Set | jchar |
Set | jshort |
SetIntField() | jint |
Set | jlong |
Set | jfloat |
SetDoubleField() | jdouble |
设置java对象属性的值
Calling Instance Methods
jmethodIDGetMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
GetMethodID得到java类中方法的ID,它只能调用类中声明为public的方法,jmethodID
为
Call<type>Method函数族使用
参数:
Name
方法在
java
类中的名字
Sig
类型签名
NativeTypeCall<type>Method(JNIEnv*env, jobject obj,
jmethodID methodID, ...);
NativeTypeCall<type>MethodA(JNIEnv*env, jobject obj,
jmethodID methodID, jvalue *args);
NativeTypeCall<type>MethodV(JNIEnv*env, jobject obj,
jmethodID methodID, va_list args);
表 3-5 |
Call<type>MethodRoutine Name | Native Type |
CallVoidMethod() | void |
CallObjectMethod() | jobject |
CallBooleanMethod() | jboolean |
CallByteMethod() | jbyte |
CallCharMethod() | jchar |
CallShortMethod() | jshort |
CallIntMethod() | jint |
CallLongMethod() | jlong |
CallFloatMethod() | jfloat |
CallDoubleMethod() | jdouble |
调用java方法通过jmethodID
指定
如果想要调用一个对象的父类方法,而不是子类的这个方法的话,就可以使用NativeTypeCallNonvirtual<type>Method(JNIEnv*env, jobject obj,
jclass clazz, jmethodID methodID, ...);
NativeTypeCallNonvirtual<type>MethodA(JNIEnv*env, jobject obj,
jclass clazz, jmethodID methodID, jvalue *args);
NativeTypeCallNonvirtual<type>MethodV(JNIEnv*env, jobject obj,
jclass clazz, jmethodID methodID, va_list args);
表 3-6 |
CallNonvirtual<type>Method Routine Name | Native Type |
CallNonvirtualVoidMethod | void |
CallNonvirtualObjectMeth | jobject |
CallNonvirtualBooleanMet | jboolean |
CallNonvirtualByteMethod | jbyte |
CallNonvirtualCharMethod | jchar |
CallNonvirtualShortMetho | jshort |
CallNonvirtualIntMethod() | jint |
CallNonvirtualLongMethod | jlong |
CallNonvirtualFloatMetho | jfloat |
CallNonvirtualDoubleMeth | jdouble |
例:
- package com.cn;
-
- public class Father {
- public void function(){
- System.out.println("Father:function");
- }
- }
1.
2.
3.
4.
5.
6.
- package com.cn;
- public class TestNativeCall {
- public native void testCall ();
- public Father p = new Child();
- public static void main(String[] args) {
-
- System.loadLibrary("nativeCode");//Java类中加载DLL,然后调用声明的native方法
- TestNativeCall tst=new TestNativeCall ();
- tst.testCall ();
- }
- }
- void Java_Com_Cn_ testCall (JNIEnv *env, jobject obj){
-
- jfieldID id_p = env->GetFieldID(clazz_TestNative,"p","Lcom/cn/Father;");
- jobject p =env->GetObjectField(obj,id_p);//取得属性
-
- jclass clazz_Father = env->FindClass("com/cn/Father");//找到Father类
- jmethodID id_Father_function =env->GetMethodID(clazz_Father,"function","()V");//获取Father类里面方法的ID
-
- //调用方法,取得的是子类方法
- env->CallVoidMethod(p,id_Father_function);
-
- //调用父类方法
- env->CallNonvirtualVoidMethod
(p,clazz_Father,id_Father_function); - }
Accessing Static Fields
jfieldID GetStaticFieldID(JNIEnv *env, jclassclazz,
const char *name, const char *sig);
GetStaticFieldID
得到java类中static属性ID,jfieldID
为
GetStatic<type>Fieldand SetStatic<type>Field函数族使用
参数:
clazz java
类对象
name
方法在
java
类中的名字
sig
类型签名
GetStatic<type>FieldRoutines
NativeTypeGetStatic<type>Field(JNIEnv*env, jclass clazz,
jfieldID fieldID);
表 3-7 | GetStatic<type>Field Family ofAccessor Routines |
GetStatic<type>Field RoutineName | Native Type |
| jobject |
| jboolean |
| jbyte |
| jchar |
| jshort |
| jint |
| jlong |
| jfloat |
| jdouble |
获取java对象静态属性的值
SetStatic<type>FieldRoutines
void
SetStatic<type>Field(JNIEnv*env, jclass clazz,
jfieldID fieldID,
NativeTypevalue);
表 3-8 | SetStatic<type>Field Family ofAccessor Routines |
SetStatic<type>Field RoutineName | Native Type |
| jobject |
| jboolean |
| jbyte |
| jchar |
| jshort |
| jint |
| jlong |
| jfloat |
| jdouble |
设置java对象静态属性的值
Calling Static Methods
GetStaticMethodID
jmethodIDGetStaticMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
GetStaticMethodID
得到java类中static方法ID,jmethodID
为
CallStatic<type>Method函数使用
NativeTypeCallStatic<type>Method(JNIEnv*env, jclass clazz,
jmethodID methodID, ...);
NativeTypeCallStatic<type>MethodA(JNIEnv*env, jclass clazz,
jmethodID methodID, jvalue *args);
NativeTypeCallStatic<type>MethodV(JNIEnv*env, jclass clazz,
jmethodID methodID, va_list args);
表 3-9 |
CallStatic<type>Method Routine Name | Native Type |
CallStaticVoidMethod() | void |
CallStaticObjectMethod() | jobject |
CallStaticBooleanMethod() | jboolean |
CallStaticByteMethod() | jbyte |
CallStaticCharMethod() | jchar |
CallStaticShortMethod() | jshort |
CallStaticIntMethod() | jint |
CallStaticLongMethod() | jlong |
CallStaticFloatMethod() | jfloat |
CallStaticDoubleMethod() | jdouble |
StringOperations
jstring NewString(JNIEnv *env, const jchar*unicodeChars,
jsize len);
Array Operations
Registering Native Methods
jint RegisterNatives(JNIEnv *env, jclassclazz,
const JNINativeMethod *methods, jint nMethods);
} JNINativeMethod
Monitor Operations
jint MonitorEnter(JNIEnv *env, jobject obj);
jint MonitorExit(JNIEnv *env, jobject obj);
NIO Support
The NIO-related entry points allow native code to accessjava.nio
direct buffers. The contents of adirect buffer can, potentially, reside in native memory outside ofthe ordinary garbage-collected heap. For information about directbuffers, please see NewI/O APIs and the specification of the java.nio.ByteBuffer class.
Three new functions introduced in JDK/JRE 1.4 allow JNI code tocreate, examine, and manipulate direct buffers:
Reflection Support