利用JNIEnv自变量,程序员可访问一系列函数。这些函数可划分为下述类别:
■获取版本信息
■进行类和对象操作
■控制对Java对象的全局和局部引用
■访问实例字段和静态字段
■调用实例方法和静态方法
■执行字串和数组操作
■产生和控制Java异常
获取版本信息
jint GetVersion(JNIEnv *env);
返回值为jint
类型,在c/c++中jint为32位数,其中高16位是主版本号,低16位是从版本号
In JDK/JRE 1.1, GetVersion()
returns 0x00010001
.
In JDK/JRE 1.2, GetVersion()
returns 0x00010002
.
In JDK/JRE 1.4, GetVersion()
returns 0x00010004
.
jclass FindClass(JNIEnv *env, const char *name);
在CLASSPATH
环境变量中
搜索目录和zip 文件中具有指定名称的类。
参数:
name
: a fully-qualified class name /
”隔开
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 by value),而其它 Java 对象都是引用传递(pass by reference),这些对象引用由 JVM 传给JNI代码,每个都有其生命周期。
JNI 函数参数中 jobject 或者它的子类,其参数都是 local reference。Local reference 只在这个 JNI函数中有效,JNI函数返回后,引用的对象就被释放,它的生命周期就结束了。若要留着日后使用,则需根据这个 local reference 创建 global reference。Global reference 不会被系统自动释放,它仅当被程序明确调用 DeleteGlobalReference 时才被回收。(JNI多线程机制)
jobject NewGlobalRef(JNIEnv *env, jobject obj);
创建一个新的全局的引用,只能使用DeleteGlobalRef
()函数销毁这个全局引用
参数:
Obj
一个本地引用或者全局引用
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
销毁全局引用
参数:
globalRef
使用
NewGlobalRef
()函数生成的全局引用
销毁本地的引用
注意:
JDK/JRE 1.1 提供了上面的 DeleteLocalRef 函数,使程序员可以手动删除本地引用。例如,如果本机代码遍历可能很大的对象或数组,并使用每个迭代中的一个元素,好的做法是下一次迭代中创建一个新的本地引用之前删除本地元素的引用。
JDK/JRE 1.2 和更高版本为本地引用生存周期管理提供了一套额外的函数。他们是下面列出的四种函数。
jint EnsureLocalCapacity(JNIEnv *env, jint capacity);
通知JVM 您将使用超过 16 个本地引用。这将允许 JVM 优化对该本机代码的本地引用的处理
jint PushLocalFrame(JNIEnv *env, jint capacity);
jobject PopLocalFrame(JNIEnv *env, jobject result);
先调用PushLocalFrame,然后创建局部引用,并对其进行处理,最后调用PushLocalFrame释放局部引用,这时Java虚拟机也可以对其指 向的对象进行垃圾回收。可以用C语言的栈来理解这对JNI API,调用PushLocalFrame之后Native代码创建的所有局部引用全部入栈,当调用PopLocalFrame之后,入栈的局部引用除了 需要返回的局部引用(PushLocalFrame和PopLocalFrame这对函数可以返回一个局部引用给外部)之外,全部出栈,Java虚拟机这 时可以释放他们指向的对象。具体的用法可以参考手册。这两个函数使JNI的局部引用由于和C语言的局部变量用法类似,所以强烈推荐使用
jobject NewLocalRef(JNIEnv *env, jobject ref);
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, jobject obj);
void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);
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 the constructor.
分配一个新的java对象,调用指定的构造函数,构造函数使用methodID
指定
jclass GetObjectClass(JNIEnv *env, jobject obj);
根据类的引用返回类的类型
jboolean IsInstanceOf(JNIEnv *env, jobject obj,
jclass clazz);
测试类的引用类型,是返回
JNI_TRUE
否则 返回JNI_FALSE
jboolean IsSameObject(JNIEnv *env, jobject ref1,
jobject ref2);
测试两个引用类型是否指向同一个空间,是返回
JNI_TRUE
否则 返回JNI_FALSE
Accessing Fields of Objects
jfieldID GetFieldID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
GetFieldID
是得到
java
类中的参数
ID
,只能调用类中声明为
public
的属性,
jfieldID
为
Get<type>Field andSet<type>Field 函数族使用
参数:
Name
属性在
java
类中的名字
Sig
类型签名
Get<type>Field Routines
NativeType Get<type>Field(JNIEnv *env, jobject obj,
jfieldID fieldID);
表 3-3 |
Get<type>Field Routine Name | Native Type |
| jobject |
GetBooleanField() | jboolean |
GetByteField() | jbyte |
| jchar |
| jshort |
GetIntField() | jint |
| jlong |
| jfloat |
GetDoubleField() | jdouble |
获取java对象属性的值
Set<type>Field Routines
void
Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,
NativeType value);
表 3-4
Set<type>Field Routine 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
jmethodID GetMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
GetMethodID得到java类中方法的ID,它只能调用类中声明为 public的方法,jmethodID
为
Call<type>Method函数族使用
参数:
Name
方法在
java
类中的名字
Sig
类型签名
NativeType Call<type>Method(JNIEnv *env, jobject obj,
jmethodID methodID, ...);
NativeType Call<type>MethodA(JNIEnv *env, jobject obj,
jmethodID methodID, jvalue *args);
NativeType Call<type>MethodV(JNIEnv *env, jobject obj,
jmethodID methodID, va_list args);
表 3-5 |
Call<type>Method Routine 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, ...);
NativeType CallNonvirtual<type>MethodA(JNIEnv *env, jobject obj,
jclass clazz, jmethodID methodID, jvalue *args);
NativeType CallNonvirtual<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, jclass clazz,
const char *name, const char *sig);
GetStaticFieldID
得到java类中static 属性ID,jfieldID
为
GetStatic<type>Field and SetStatic<type>Field函数族使用
参数:
clazz java
类对象
name
方法在
java
类中的名字
sig
类型签名
GetStatic<type>Field Routines
NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz,
jfieldID fieldID);
表 3-7 | GetStatic<type>Field Family of Accessor Routines |
GetStatic<type>Field Routine Name | Native Type |
| jobject |
| jboolean |
| jbyte |
| jchar |
| jshort |
| jint |
| jlong |
| jfloat |
| jdouble |
获取java对象静态属性的值
SetStatic<type>Field Routines
void
SetStatic<type>Field(JNIEnv *env, jclass clazz,
jfieldID fieldID,
NativeType value);
表 3-8 | SetStatic<type>Field Family of Accessor Routines |
SetStatic<type>Field Routine Name | Native Type |
| jobject |
| jboolean |
| jbyte |
| jchar |
| jshort |
| jint |
| jlong |
| jfloat |
| jdouble |
设置java对象静态属性的值
Calling Static Methods
GetStaticMethodID
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
GetStaticMethodID
得到java类中static方法 ID,jmethodID
为
CallStatic<type>Method 函数使用
NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz,
jmethodID methodID, ...);
NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz,
jmethodID methodID, jvalue *args);
NativeType CallStatic<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 |
String Operations
jstring NewString(JNIEnv *env, const jchar *unicodeChars,
jsize len);
Array Operations
Registering Native Methods
jint RegisterNatives(JNIEnv *env, jclass clazz,
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 access java.nio
direct buffers. The contents of a direct buffer can, potentially, reside in native memory outside of the ordinary garbage-collected heap. For information about direct buffers, please see New I/O APIs and the specification of the java.nio.ByteBuffer class.
Three new functions introduced in JDK/JRE 1.4 allow JNI code to create, examine, and manipulate direct buffers:
Reflection Support