有关Android JNI开发必须知道的

      在Android开发中,有时候我们需要依赖设备上的某些特定功能,例如打印功能,那么就需要调用一些“so”文件,这样才能使用打印功能。这些“so”文件其实是用c/c++编写的代码,而Java为这样的开发需求提出了自己的标准,就是 JNI开发。

    本篇文章只是简单介绍JNI开发有关知识,如果需要开发JNI,请使用NDK以及查阅有关JNI开发文档。

一、概述。

1.什么是JNI?

 JNI全称为Java Native Interface(JAVA本地调用)。从Java1.1开始,JNI成为java平台的一部分,它允许Java代码和其他语言写的代码(如C&C++)进行交互。并非从Android发布才引入JNI的概念的。

         JNI是一个协议,这个协议用来沟通java代码和外部的本地代码(c/c++),外部的c/c++代码也可以调用java代码。JNI开发属于Java开发的范围,有关JNI开发,就得参考Java开发文档了!

2.为什么使用JNI?

(1).效率上 C/C++是本地语言,比java更高效;
(2).代码移植,如果之前用C语言开发过模块,可以复用已经存在的c代码;
(3).java反编译比C语言容易,一般加密算法都是用C语言编写,不容易被反编译。

二、JNI的数据类型。

我们知道Java的数据类型是跟C/C++的数据类型是不一样的,而JNI是处于Java和Native本地库(大部分是用C/C++写的)中间的一层,JNI对于两种不同的数据类型之间必须做一种转换,所以在JNI跟Java之间就会有数据类型的对应关系。

      JNI的数据类型包含两种:基本类型和引用类型。基本类型主要有jboolea、jchar、jbyte等,他们和Java中的数据类型的对应关系如下图所示,


      JNI中的引用类型主要有类、对象和数组,他们和Java中的引用类型的对应关系如下图所示,



三、JNI类型签名。

      JNI方法签名。

    为什么会有方法签名这种东西呢?这是因为Java这边支持函数重载,即虽然参数不一样,但是方法名一样,那么在JNI层它们的方法名都会是一样的,那JNI也会犯迷糊了,得找哪个呢?
    不过也正是因为其参数类型是不一样的,所以就出现了方法签名,利用方法签名和方法名来唯一确定一个JNI函数的调用。
    既然方法签名是基于参数类型的不同而形成的,首先要知道Java各数据类型对应的签名是什么,也就是所谓的类型签名。

      JNI的类型签名标识了一个特定的Java类型,这个类型即可以是类和方法,也可以是数据类型。

    1.类的签名比较简单,它采用“L+包名+类名+;”的形式,只需要将其中的"."替换为"/"即可。比如java.lang.String,它的签名为Ljava/lang/String;,注意末尾的";"也是签名的一部分。

    2.基本类型的签名采用一系列大写字母来标识,如下图所示,


      基本数据类型的签名是有规律的,一般为首字母的大写,但是boolean和 long比较特殊,在使用时需要留意

    3.对象和数组签名。

    对象和数组的签名复杂一些。对于对象来说,它的签名就是对象所属的类的签名,比如String对象,它的签名为Ljava/lang/String;。

    对于数组来说,其为 :  [ + 其类型的域描述符。比如int数组,其类型为int,而int的签名为I,所以int数组的签名就是[I,例如,

    int[ ]     其描述符为[I
    float[ ]   其描述符为[F
    String[ ]  其描述符为[Ljava/lang/String;
    Object[ ]类型的域描述符为[Ljava/lang/Object;
   对于多维数组则是 n个[ +该类型的域描述符 , N代表的是几维数组。例如:
    int  [ ][ ] 其描述符为[[I
    float[ ][ ] 其描述符为[[F

    4.方法签名。

    方法的签名为(参数类型签名)+ 返回值类型签名。对于,没有返回值的,用V(表示void型)表示。举个例子,

                Java层方法                                               JNI函数签名
                String test ( )                                          Ljava/lang/String;
                int f (int i, Object object)                            (ILjava/lang/Object;)I
                void set (byte[ ] bytes)                                ([B)V

四、JNI函数命名规则。

javah生成的c/c++头文件的时候,会对java中定义的 native 函数生成对应的jni层函数,如下:

#include <cn_xinxing_jnitest_CalculateUtils.h>

JNIEXPORT jstring JNICALL  Java_cn_xinxing_jnitest_CalculateUtils_getStringFromNative
        (JNIEnv * env, jobject obj, jstring str){
    return str;
}
    首先函数名的格式遵循如下规则: Java_包名_类名_方法名。比如 Java_cn_xinxing_jnitest_CalculateUtils_getStringFromNative
        (JNIEnv * env, jobject obj, jstring str),其中cn_xinxing_jnitest是包名,CalculateUtils是类名,getStringFromNative是方法名,jstring 是方法的String类型的参数。JNIEXPORT、JNICALL、JNIEnv和jobject都是JNI标准中所定义的类型或者宏,它们的含义如下:

(1).JNIEnv * :表示一个指向JNI环境的指针,可以通过它来访问JNI提供的接口方法;

(2).jobject: 表示Java对象种的this;

(3).JNIEXPORT和JNICALL: 它们是JNI中所定义的宏可以在jni.h这个头文件中查找到。

    如果不这样命名,当把动态库加载进DVM的时候,通过JNIEnv *指针去查找Java Native方法对应的JNI方法的时候,就会找不到了。

五、JNI  调用流程。(出自 Android的NDK开发(1)————Android JNI简介与调用流程

众所周知,Android的应用层的类都是以Java写的,这些Java类编译为Dex文件之后,必须靠Dalvik虚拟机( Virtual Machine)来执行。假如在执行java程序时,需要载入C&C++函数时,Dalvik虚拟机就会去加载C&C++的库,(System.loadLibrary("libName");)让java层能顺利地调用这些本地函数。需要清楚一点,这些C&C++的函数并不是在Dalvik虚拟机中运行的,所以效率和速度要比在Dalvik虚拟机中运行得快很多。
    Dalvik虚拟机成功加载库之后,就会自动地寻找库里面的JNI_OnLoad函数,这个函数用途如下:
(1)告诉Dalvik虚拟机此C库使用哪一个JNI版本。如果你的库里面没有写明JNI_OnLoad()函数,VM会默认该库使用最老的JNI 1.1版本。但是新版的JNI做了很多的扩充,也优化了一些内容,如果需要使用JNI的新版功能,就必须在JNI_OnLoad()函数声明JNI的版本。如

result = JNI_VERSION_1_4;  
当没有JNI_OnLoad()函数时,Android调试信息会做出如下提示(No JNI_OnLoad found)
04-29 13:53:12.184: D/dalvikvm(361): Trying to load lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98
04-29 13:53:12.204: D/dalvikvm(361): Added shared lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98
04-29 13:53:12.204: D/dalvikvm(361): No JNI_OnLoad found in /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98, skipping init
(2)因为Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数,所以我们可以在JNI_OnLoad()里面进行一些初始化工作,如注册JNI函数等等。注册本地函数,可以加快java层调用本地函数的效率。
另外:与JNI_OnLoad()函数相对应的有JNI_OnUnload()函数,当虚拟机释放该C库时,则会调用JNI_OnUnload()函数来进行善后清除动作。

六、总结。

      本篇文章只是简单介绍了JNI的基础知识,文章是笔者读书以及阅读其他文章的一个总结。如果要开发JNI,请看我的下篇文章,你不知道的Android NDK开发。部分内容来自《Android开发艺术探索》

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值