[置顶] 基于Android2.3.5系统:JNI与HAL实例解析[一]

时间:2022-09-09 05:09:21
***************************************************************************************************************************
作者:EasyWave                                                                                                           时间:2015.01.25
类别:Android系统-基于Android2.3.5系统:JNI与HAL实例解析[一]                    声明:转载,请保留链接

注意:如有错误,欢迎指正。这些是我学习的日志文章......

***************************************************************************************************************************

一:Android系统下JNI简介

       Android系统下的JNI的全称是:Java Native Interface (JNI),JNI标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。

二:为什么Java要推出JNI

       JNI在Android系统中有着广泛的应用。Android系统底层都是C/C++实现的,上层提供的API都是Java的,Java通过JNI调用底层的实现。

  • Java世界虚拟机使用Native语言编写的,虚拟机运行在具体的平台上,虚拟机本身无法做到与平台无关,jni可以对java层屏蔽不同平台操作的差异,这样就能实现java本身平台无关特性。
  • 适应已经用Native语言实现的技术。
  • 一些效率的问题需要Native语言实现。

       在Java中使用JNI主要是为了以上几点而实现的,在Anroid中的API多媒体接口MediaPlayer类,其实底层通过JNI调用libmedia库。由于JNI的存在可以让我们重用很多已经存在C/C++的库,省去了重复开发的麻烦,并且可以利用很多开源的库(Android库中就有很多开源库,比如libjpeg,libpng等等),并且让我们开发的程序更有效率(C/C++代码发挥硬件最佳性能)。

三:Java中JNI调用过程

      在Java的世界里,JNI对于应用本身来说,可以看做一个代理模式,而对于开发者来说,需要使用C/C++来实现一个代理程序来实际操作目标原生函数,Java程序中则是JVM通过加载并调用JNI程序间接地调用目标原生函数。其调用的示意图如下:

[置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]

图一:Android下JNI调用过程(Load DLL,DLL根据不同的环境而定

      Android系统Java下JNI程序开发的一般操作步骤如下:

  • 编写JAVA中的调用类;
  • 用javah生成C/C++原生函数的头文件;
  • C/C++中调用需要的其他函数功能,实现原生函数;
  • 将项目依赖的所有原生库和资源加到java项目的路径中;
  • 生成JAVA程序;
  • 发布JAVA应用和so库;

四:Android系统下JNI语法

         Android系统Java下的JNI对于应用本身来说,可以看做一个代理模式,对于开发者来说,需要使用C/C++用一个代理程序来实际操作目标原生函数,其一,基本的数据类型转换,如下所示:

[置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]

图二:基本数据类型转换表

       备注:符号属性是指在JNI环境下

       其二,引用数据类型转换表,如下所示:

[置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]

图三:引用数据类型转换表

五:Java下JNI注册函数

       Java下的JNI命名规则:JNI层需要将JAVA函数名称(包括包名)中的“.”换成“_”,用这种方式可以找到自己JNI层的路径以及文件。注册Native函数有两种方法:静态注册和动态注册。静态注册静态就是根据函数名来建立java和jni函数之间的关联,而且要求jni层函数的名字必须遵循特定的格式,其缺点在于:需要编译所有的java声明的类;javah生成的jni层函数特别长;初次调用native函数时要根据名字搜索对应的jni层函数来建立关联联系,这样影响效率。因此这里只详细的讲解动态注册的方式,Java native与JNI通过JNINativeMethod的结构来建立联系,其结构内容如下: 

typedef struce {   
const char* name; //java中native函数的名字,不用携带包的路径
const char*signature; //java函数的签名信息,用字符串表示,是参数类型和返回值类型的组合
void *fnptr; //JNI层对应的函数指针,注意他是void*的类型。
} JNINativeMethod;
          用一个Anroid系统下的实例来感性的认识和说明一下,(请到网上去下载Anroid2.3.4的OK6410的Anroid代码,跟本文的路径一致,因为我自己把这个Android2.3.4的OK6410移植到Android2.3.5的平台上)路径为android2.3.5/frameworks\base\services\forlinx_pwm_jni\PwmJniService.cpp,这是一个PWM控制的JNI,具体的如下所示:

[置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]

图四:JNINativeMethod方法       Android提供一个RegisterNatives(JNIEnv *env, Const char* className,  Const JNINativeMethod* gMethods,  int numMethods)来实现这个功能,其实还有一个在AndroidRuntime.cpp中也实现了一个RegisterNatives函数,两者的功能是相同的,如下所示:

[置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]      我们来看看PwmJniService.cpp函数中的JNI动态注册吧,如下所示:

[置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]

        在JNI_OnLoad函数中,会去调用register_forlinx_server_PwmService()这个函数,而这个函数的定义如下所示:

[置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]

     一起来看看这行代码:static const char* const kClassName ="forlinx_pwm_server/server/PwmService";,就是通过kClassName找到相应的库的,而这个PwmService就是在Android2.3.5/frameworks\base\services\forlinx_pwm_server\server\PwmService.java文件,如下所示:

[置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]

        也就是说Java层通过这种手段实现了Java层去调用C/C++层的库,这样就将Anroid底层给封装起来了,而在Android的JNI层会去调用Android的HAL层,而HAL层调用的就是Linux的驱动!!

六:JNI的垃圾回收
       Java中创建的对象最后由垃圾回收器来回收和释放内存的,而对于JNI,直接对对象进行引用后不会增加引用计数值,当从JNI返回后在JNI引用的对象有可能被垃圾回收器回收,而在JNI下面记录的指针有可能是野指针,为了解决此类问题,JNI规范提供如下三类引用:

  • Local Refrence:本地引用,在JNI层函数中使用的非全局变量都是采用Local Refrence,它包含函数调用时传入的jobject和在JNI层函数中创建的jobject,其最大的特点是一旦JNI层函数返回,这些jobject就可能被垃圾回收了。
  • Global Refrence:全局引用,这类引用如果程序不主动调用销毁接口将一直驻留在内存中。
  • Weak Global Refrence:弱全局引用,在使用过程中有可能被释放,调用JNIEnv中IsSameObject函数来判断是否仍然存在。
七:Java下JNI函数的签名信息        Android系统下Jave的JNI的签名Signature的书写规则如下:  ()中的字符表示参数,后面的则代表返回值,如“()V”就表示void  func(), “(II)V”表示void func(int, int)等。具体的每个字符对应关系如下: [置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]
        如果java函数的参数是class,则以“L”开头,以“;”结尾,中间是用“/”隔开的包及类名,对应的C函数名的参数则为jobject,一个例外是String类,其对应类为jstring。如下所示:
          Ljava/lang/String;    String     jstring
Ljava/net/Socket; Socket jobject
        以android2.3.5/frameworks\base\services\forlinx_pwm_jni\PwmJniService.cpp中的static const JNINativeMethod gMethods[]数组函数为例子,如下所示:
[置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]
        选取forlinx_set_freq函数来说明JNI签名的问题,在上图中可以看到  { "_set_freq",     "(I)Z", (void *)forlinx_set_freq }, 这样的定义方式,对照上表中的签名解析表格,我们就一目了然啦。forlinx_set_freq函数的定义如下所示: [置顶]        基于Android2.3.5系统:JNI与HAL实例解析[一]
        可以看出来forlinx_set_freq的返回值类型为:jboolean,而jboolean所代表的JNI签名为:Z,而jint freq是forlinx_set_freq函数中需要设置的一个值,而jint所代表的JNI的签名为:I 因此forlinx_set_freq函数的签名信息为:(I)Z