为什么需要JNI?
成都创新互联是一家专业提供大通企业网站建设,专注与成都网站设计、做网站、H5高端网站建设、小程序制作等业务。10年已为大通众多企业、政府机构等服务。创新互联专业网站建设公司优惠进行中。
android这个庞大的系统从下到上主要由linux内核,C/C++库,java应用程序框架,java应用程序组成。这就涉及到一个问题,C/C++库如何与java应用有交集,或者说能相互调用,要解决这个问题,就需要JNI登场了。
JNI调用机制分析
JNI--java native interface,翻译成中文是java本地接口,所谓的“本地”是指C/C++库一层的C/C++语言(以下统称C)。
上文提到,JNI是为解决C和Java相互调用的问题而诞生的。C和Java相互调用无非就是两个方面,Java调用C和C调用Java。
Java调用C函数
如果你只定义了一个函数,而将它的实现交给C,那么就将它定义为native类型好了。你或许会问了,我用Java定义了一个函数,但我却没用Java实现之,当我调用这个函数的时候,Java编译器不会报错吗?答案是否定的,Java编译器在遇到native类型的函数时,不会关心该函数的具体实现,相当于native类型告诉Java编译器,“喂,老兄,我实现了,只不过我不是用你的语言(Java)实现的,我是用别的语言(C)实现的”,所以编译时Java编译器不会报错,只不过在调用native类型的函数前,程序员必须把C生成的动态库装载进内存,否则程序会因为找不到相应的native方法而出错。
Java和C本不是同种语言,硬要让它们能相互调用的话,我们就要遵循某种规范(就像ARM汇编和C相互调用时也要遵循某种规范一样)。
规范:methodname_C = Packagename + methodname_Java。什么意思呢?在C中定义的函数名称 = 包名 + 在Java中定义的函数名称。还不明白?我们来举个例子来看一下。
java:private nativefinal void init()
C:static void android_content_AssetManager_init(JNIEnv *env,jobject clazz)
可以看到对应的C函数多了两个参数,看起来莫名其妙,但这其实能让C访问Java对象或函数,JNIEnv对象是一个Java虚拟机所运行的环境,jobject是调用该函数的对象。我们稍后会讲到。
为什么C中的函数要加上包名呢?这不是多此一举吗?
这是有必要的,这是为了区分开不同包的两个同名函数,能够让Java编译器找到正确的那个函数。事实上,Java调用native函数时,编译器会向native引擎传递调用者的包名,以及函数名称,还有参数类型,以便可以根据这些信息找到正确的本地的那个函数。
C调用Java函数或者访问Java变量
正如Java调用C函数一样,Java把类名、函数名称和参数类型传递给native引擎,然后由native引擎处理(正确找到并调用)C函数。同理,C调用Java时,也需要把想要访问的类名、函数名称和参数传递给Java引擎。步骤如下:
1)获取Java对象的类
jclass cls = env->GetObjectClass(jobject),还记得env和jobject吗?没错,就是那两个参数。现在知道那两个参数的重要性了吧。不过,这也意味着C调用Java函数只能在Java调用C函数体中进行。
2)获取Java函数的id值
jmethodId mid = env->GetMethodId(cls,"methodname","(Ljava/lang/String;)V"),前两个参数不必说,第三个参数值得注意,它代表了Java函数的参数和返回值,参数在括号之中,返回值在括号之外。
3)找到了函数后,就可以调用该函数了
env->CallXXXMethod(jobject,mid,ret),其中XXX代表了返回值的类型,