前言小结1.jni和ndk的基础知识点在上一篇文章中已经提到,我不懂的,可以上公众号的首页看看;2、jni中常用的方法如:类、方法、数组、字符串等,前面也有讲解;3、本文讲解了jni中函数的注册和c++调用java的知识点;一、JNI函数注册1、JNI函数注解知识点介绍JNI技术是Java世界与Native世界沟通的桥梁。具体来说,Java层的代码如何调用Native层的代码呢?我们都知道,在调用native方法之前,必须先调用System.loadLibrary接口加载一个实现了native方法的动态库,否则会抛出java.lang.UnsatisfiedLinkError异常。那么,在Java中调用native方法时,JVM如何才能在动态库中正确找到C/C++实现的native函数呢?JVM找到native方法有两种方式;根据JNI规范的命名规则,调用JNI提供的RegisterNatives函数,将native函数注册到JVM中;第一种方式,可以使用javah工具,根据Java类中定义的native方法和JNI规范文件的命名规则,自动生成JninativeC/C++header;第二种方法需要在本地库的JNI_OnLoad函数中调用RegisterNatives进行动态注册;JNI函数注册是将Java层声明的Native方法与实际的Native函数进行绑定的实现,也就是说只要JNI函数注册机制注册了本地的地方,Java层就可以直接调用这些本地定义的方法。对应以上两种JVM搜索native方法的方式,JNI函数注册方式一般分为静态注册和动态注册。2、静态注册原则:根据函数名建立java方法和JNI函数的一一对应关系;实现过程:编写java代码;使用javah命令生成对应的.h文件;执行.h中的声明;缺点:编写不方便,JNI方法名必须遵守规则且名字很长;书写过程步骤多,不方便;程序运行效率低,因为第一次调用native函数时,需要根据函数名在JNI层查找对应的native函数,然后建立对应关系,是一个耗时的过程;publicclassTest{static{System.loadLibrary("native-lib");}publicnativeStringtextFromJni();}使用javah生成对应的native方法头文件。#include#ifndef_Included_test#define_Included_test#ifdef__cplusplusextern"C"{#endifJNIEXPORTjstringJNICALLJava_test_Test_textFromJni(JNIEnv*,jobject);#ifdef__cplusplus}#endif3、动态注册对应上面的静态注册方式,还有JNI的动态注册function方式,即动态注册。动态注册是在Java层调用System.loadLibrary方法加载so库时,会调用native库的JNI_OnLoad函数,通过调用JNI_OnLoad函数中的RegisterNatives函数完成native方法的注册。原理:使用RegisterNatives方法注册java方法和JNI函数的一一对应关系;实现过程:使用结构体JNINativeMethod数组记录java方法与JNI函数的对应关系;实现JNI_OnLoad方法,加载动态库后进行动态注册;调用FindClass方法获取java对象;调用RegisterNatives方法传入java对象、JNINativeMethod数组、注册号完成注册;优点:流程更清晰可控;更高的效率;JNINativeMethod结构体用于描述局部方法结构,其定义如下:typedefstruct{constchar*name;//Java方法名constchar*signature;//Java方法签名void*fnPtr;//jni局部对应的函数指针方法}JNINativeMethod;结构体的第一个参数名是java方法名;第二个参数签名用于描述方法的参数和返回值;第三个参数fnPtr是指向jni函数的函数指针;其中,第二个参数签名使用字符串记录方法的参数和返回值,具体格式如“()V”和“(II)V”,分为两部分。参数在括号中表示,返回值在括号右侧表示;①、数据类型映射基本数据类型②。数组引用如果类型是一维数组,请遵循下表。如果是二维数组或更高维数组,则对应的原生类型为jobjectArray,字段描述符中使用'['的个数来表示维度③。对象引用类型是对于其他的引用类型,即java中的对象,其映射规则为④。如果对象数组引用类型是一维数组,则遵循下表。如果是二维数组或者更高维数组,对应的native类型是jobjectArray,域描述符使用'['的个数来表示维度。在Java文件中定义本地方法,加载本地so库packagetest.jnitest;publicclassTest{static{System.loadLibrary("native-lib");}publicnativeStringtextFromJni();}在JNI_OnLoad函数中注册本地方法字符串;",(void*)textFromJni}};intregisterMethod(JNIEnv*env){jclasstest=env->FindClass("cc/ccbu/jnitest/Test");returnenv->RegisterNatives(test,gMethods,sizeof(gMethods)/sizeof(gMethods[0]));}JNIEXPORTjintJNICALLJNI_OnLoad(JavaVM*vm,void*reserved){JNIEnv*env=NULL;if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!=JNI_OK){returnJNI_ERR;}if(registerMethod(env)!=JNI_OK){returnJNI_ERR;}returnJNI_VERSION_1_6;}注意:在JNI_OnLoad函数的最后,我们必须有一个返回值,它必须是JNI_VERSION_1_4或JNI_VERSION_1_6,这是JNI的版本号.我们必须返回正确的版本号,否则无法加载系统;4、c++调用java详解(1)找到java对应的Class(2)找到要调用的方法的methodID(3)在C语言中调用对应的方法①。在JAVA层通过本地方法创建相似对象的步骤:一、通过对象获取类二.通过类III获取类的构造方法的ID。根据方法ID和类,新建对象JNIEXPORTvoidJNICALLJAVA_nativeMethod(JNIEnv*env,jobjectthiz,jinti){...jclassclazz=(*env).GetObjectClass(thiz);jmethodIDmid=(*env).GetMethodID(clazz,"","()V");joobjectobj=(*env).NewObject(clazz,mid);...返回;}②.通过C/C++创建不同类对象的步骤:一、通过FindClass方法获取需要的类二.通过类III获取类的构造方法的ID。根据方法ID和类,新建对象JNIEXPORTvoidJNICALLJAVA_nativeMethod(JNIEnv*env,jobjectthiz,jinti){...jclassclazz=(*env).FindClass("com/x/test/Test");//参数是类路径jmethodIDmid=(*env)。GetMethodID(clazz,"","()V");jobjectobj=(*env).NewObject(clazz,mid);...return;}③获取上下文环境JNIEnv如果找不到上下文JNIEnv获取boolAttachCurrentThread(JavaVM*vm,JNIEnv**p_env){boolbAttached=false;switch(vm->GetEnv((void**)p_env,JNI_VERSION_1_4)){caseJNI_OK:break;caseJNI_EDETACHED:if(vm->AttachCurrentThread(p_env,0)<0){LOGD("%s:testfailed!",__func__);returnfalse;}else{bAttached=true;}break;caseJNI_EVERSION:LOGE("Invalidjavaversion");break;}returnbAttached;}总结JNI中函数注册的两种方式很常见,在实际应用中可以使用,他们必须得到适当的理解;下一篇本章将继续讲解JNI的知识点和高级应用。本文转载自微信公众号《Android开发编程》。