JNI知识总结

Jni使用步骤(Windows平台下):

1.创建一个类,该类中native声明的方法则表明调用本地库的本地方法(该计算机)

2.使用javac编译该类

3.利用javah产生jni的头文件

4.用本地代码(c++)实现头文件中定义的方法

5.将本地代码编译后生成的dll文件(native library)复制到之前java项目中

6.java代码中加载本地库文件(dll文件),运行java代码,执行定义的native方法

(下面项目请参考eclipse codeforces jni Test1和 VS ConsoleApplication2.sln)

public class Test1 {

public native void sayHello();

static{

System.loadLibrary("ConsoleApplication2");        //加载C++生成的dll文件

}

public static void main(String[] args) {

Test1 k = new Test1();

k.sayHello();

}

}

本地代码:

JNIEXPORT void JNICALL Java_com_jni_Test1_sayHello(JNIEnv *env, jobject obj)

{

printf("helloWorld\n");

return ;

}

JNIEnv指的是JNIEnv结构的指针(可以调用JNI各个方法)jobject指的是Test1对象自身,即javathis指针

当本地方法作为一个静态方法时,jobject指向所在类,当本地方法作为一个实例方法时,jobject相当于对象本身

JNIEvn类中有几个函数取得jclass:

1.jclass FindClass(const char * clsName)----传入完整类名

2.jclass GetObjectClass(jobject obj)   ------传入类示例

native方法中声明的参数类型,在JNI中都有对应的类型

Java Native(jni.h)   基本数据类型

boolean jboolean

byte jbyte

char jchar

short jshort

int jint

long jlong

float  jfloat

double jdouble

jni中对象的基类是jobject,为方便起见,还定义了jstringjclassjobjectArray等结构,它们都继承于jobject

JNI处理异常:JNI发生异常,不会改变代码执行轨迹,所以当返回NULL,要及时返回,或马上处理

eg.  JNIEnv->newStringUTF构造java.lang.String,如果此时没有足够的内存,newStringUTF将抛出OutOfMemoryError异常,同时返回NULL

String操作函数中有很多类似这样的:如有两个函数GetStringLengthGetStringUTFLength,前者是Unicode编码长度,后者是UTF编码长度

GlobalRef:当你需要在JNI层维护一个java对象的引用,而避免该对象呗垃圾回收时,使用newGlobalRef告诉VM不要回收此对象。当本地代码最终结束对该对象的引用时,用DeleteBlobalRef释放

LocalRef:DeleteLocalRef释放对象的LocalRef

GlobalRefLocalRef的区别:

(1)大部分JNI函数都会创建LocalRef,如NewObject创建一个实例,并返回一个纸箱该实例的LocalRef

(2)LocalRef只在本线程的native method中有效.一旦native method返回,LocalRef将被释放。不要缓存一个LocalRefstatic存放该LocalRef),并企图在下次进入该JNI方法时调用,因为这时LocalRef在这之前已经被释放了

(3)释放GlobalRef前,可以在多个本地方法调用过程和多线程中使用GlobalRef所引对象,另外GlobalRef必须通过newGlobalRef由我们主动创建,而LocalRef一般自动创建(返回值为jobject/jclass等)

(4)当你不再使用GlobalRef所指对象,及时调用DeleteGlobalRef释放对象,否则,GC价格不回收该对象

两种方式令LocalRef无效:

(1)native method返回,java VM自动释放LocalRef

(2)deleteLocalRef主动释放

java层的fieldmethod,不管它是public,还是packageprivateprotected,从JNI中都可以访问到

访问对象成员步骤:

(1)通过getFieldID得到对象成员ID(实例成员,其实静态成员类似getStaticFieldID,

fid = env->getFieldID(env, cls, "s", "Ljava/lang/String;");  //cls是通过getObjectClassobj对象得到cls

jstr = env->getObjectField(env, obj, fid);     //通过在对象上调用该方法获得成员的值

JNI sign                  签名             

boolean                   Z

byte                      B

char                      C

short                     S

int   I

long   J

float   F

double   D

object                    Ljava/lang/String;

array                     [I[Ljava/lang/Object;

method                   (参数1签名,....)返回类型签名

type[]   [ type

:   long f(int n, String s, int[] arr)

      签名为: "(ILjava/lang/String;[I)J"    

注意:

1)类描述符开头的'L'与结尾的';'必须要有

2)数组描述符,开头的'['必须有

3)方法描述符规则:"参数描述符间没有任何分隔符号

访问对象方法的步骤:

(1)通过getMethodID在给定类中查询方法,(函数参数)查询基于方法名称和签名

(2)本地方法调用CallVoidMethod,该方法表明被调用的Java方法返回值为void,如CallIntMethod

访问对象静态方法其实和访问对象方法大致一样:

(1)通过getStaticMethodID在给定类中查询方法,(函数参数)查询基于方法名称和签名

(2)本地方法调用CallStaticVoidMethod,该方法表明被调用的Java方法返回值为void,如CallStaticIntMethod

静态方法与实例方法的不同在于,前者传入参数为jclass,后者为jobject

调用被子类覆盖的父类方法:JNI支持用CallNonvirtualMethod

(1)getMethodID获得method ID

(2)调用CallNonvirtualVoidMethod(可以调用构造函数),CallNonvirtualIntMethod

构造函数的名称都是"",签名里可以有各样的参数

cid = env->getMethodID(env, cls, "", "([C)V");    //获得cls,参数为char[]的构造函数

Push/Pop LocalFrame常被用于管理LocalRef。在进入本地方法时,调用一次PushLocalFrame,并在本地方法结束时调用PopLocalFrame,这种方法执行效率非常高

PopLocalFrame会一次性释放所有的LocalRef

本地代码处理异常的两种方式:

(1)本地代码可以立即返回(遇到NULL情况,释放相应资源,然后直接return),并在调用者中处理异常

(2)当有异常发生,调用ExceptionDescribe打印调用堆栈,然后用ExceptionClear清空异常,然后自己做重新抛出等策略

JNI知识点:

__cpluspluscpp中的自定义宏,表示一个数字,一般cpp文件都会定义了这个宏.

__stdcall调用约定用于调用Win32 API函数

在动态链接库中__declspec(dllexport)管导出,__declspec(dllimport)管导

extern "C",还得从cpp中对函数的重载处理开始说起。在c++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++C对产生的函数名字的处理是不一样的.

C++没有加extern  C 时名字前有其他英文,而加了extern C名字不会改变。

extern "C"是使C++能够调用C写作的库文件的一个手段

JNI用处:

1.你的Java代码,需要得到一个文件的属性。但是你找遍了JDK帮助文档也找不到相关的API

2.在本地还有一个别的系统,不过他不是Java语言实现的,这个时候你的老板要求你把两套系统整合到一起。

3.你的Java代码中需要用到某种算法,不过算法是用C实现并封装在动态链接库文件(DLL)当中的。

JNI缺点:

1.因为JNI有一个Native这个特点,一点有项目用了JNI,也就说明这个项目基本不能跨平台了,可移植性差

2.JNI调用是相当慢的,在实际使用的之前一定要先想明白是否有这个必要。

3.因为C++C这样的语言非常灵活,一不小心就容易出错,比如我刚刚的代码就没有写析构字符串释放内存,对于java developer来说因为有了GC 垃圾回收机制,所以大多数人没有写析构函数这样的概念。所以JNI也会增加程序中的风险,增大程序的不稳定性。

你可能感兴趣的:(JNI知识总结)