Android JNI技术总结

10 篇文章 0 订阅
2 篇文章 0 订阅






JNI技术总结








目 录











1概述

1.1简介

Java本机接口(JavaNative Interface (JNI))是一个本机编程接口,它是Java软件开发工具包(JavaSoftwareDevelopment Kit(SDK))的一部分,它提供了若干的API,实现了和Java和其他语言的通信(主要是C&C++)。

JNI允许Java代码使用以其它语言(譬如CC++)编写的代码和代码库。

同时,我们知道Java是一种平台无关性的语言,平台对于上层的java代码来说是透明的,所以在多数时间我们是不需要JNI的,但是当你遇到如下三种情况:

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

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

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

对于上述的三种情况,如果没有JNI的话,那就会变得异常棘手。就算找到其他解决方案,也比较费时费力,且会增加开发和维护的成本。

1.2系统环境

系统环境是指本地操作系统环境,它有自己的本地库和CPU指令集,本地程序【NativeApplication】是使用C/C++语言编写的,然后被编译成能在本地系统环境中运行的二进制代码,并和本地库链接在一起。所以本地程序和本地库一般都会依赖于一定的本地系统环境,也就是说在一个系统环境中编译出来的C程序无法在另一个系统中运行。所以当我们需要在多个系统中统一对上层的接口时,JNI的作用就体现出来了。

JNI最常见的两个应用:从Java程序调用C/C++,以及从C/C++程序调用Java代码.

2JNI的作用

2.1JNI的角色

由于JNI的强大特性,允许我们使用JAVA平台的同时,还可以复用之前的本地C/C++代码。作为虚拟机实现的一部分,JNI允许JAVA与本地代码间的双向交互。

其交互框架如下:








JNINative程序的交互:

  1. 使用JNI来实现Native方法,并在JAVA程序中来调用它们.

  2. JNI支持一个调用接口【invocationinterface,允许你把一个JVM嵌入本地程序中,本地程序也可以链接一个实现了JVM的本地库,然后使用“调用接口”从而执行JAVA语言程序。

2.2JNI的副作用

JAVA语言以跨平台而著称,但在使用JNI后,它的跨平台性也就丧失了:

  1. 程序不再跨平台

如果要跨平台,则需在不同系统环境中对本地代码进行重新编译

  1. 程序不再绝对安全

本地代码的不当使用可能会导致整个程序或系统崩溃.

为了尽可能减少JNI的副作用,通常让本地方法集中在较少的几个类中,以降低JAVA与本地程序的耦合性。

2.3JNI的使用场景

由于JNI具有副作用,所以在某些非必须使用JNI时,应考虑是否可用其他方法来达到与本地代码交互的目的。如:JAVA与本地代码使用TCP/IPIPC进行交互等,通常在JAVA程序和本地代码处于不同的线程,或者不同的机器上时,即可不使用JNI。这样在本地程序出现问题时不至于影响到JAVA程序。

但在下面的场景中,同一进程内JNI的使用则无法避免:

  1. JAVA程序中使用到了JAVAAPI不提供的特殊系统环境才有的特征,而跨进程操作不现实。

  2. 需要访问已有的本地库,但又不想付出跨进程调用时的代价,如:效率,内存等

  3. JAVA程序中的一部分代码对处理效率要求非常高,如:算法计算,图形渲染等。

3数据类型

C/C++代码中,编译器会根据所处的平台为一些基本的数据类型分配长度,因此就存在平台的不一致性,而在JAVA中则没有这个问题因为有JVM的存在,基于这个原因,就需要对JAVAC++中的基本数据类型有一个Mapping,以保持数据类型的一致性。

C/C++JAVA的基本数据类型映射表如下:


JAVA类型

Native类型

JNI别名

Int

long

jint

long

_int64

jlong

byte

signedchar

jbyte

boolean

unsignedchar

jboolean

char

unsignedshort

jchar

short

short

jshort

float

float

jfloat

double

double

jdouble

object

_jobject*

jobject


其实,JNI的设计者已经帮我们取好了相应的别名以方便记忆。如果想了解一些更加细致的信息,可以去看一下jni.h这个头文件,各种数据类型的定义以及别名就被定义在这个文件中。

除了Java声明中的一般参数以外,所有这些函数的参数表中都有一个指向JNIEnvjobject的指针。

4JNI例程

Android通过JNI来实现Java层调用C层代码,我们必须创建c代码,然后编译so库,编写JNI中的代码,最后Java层通过System.loadLibrary()方法加载so动态库,即可实现。

编写一个具有so,jni , java整体模块,一般可以这样来做:

(1)编写Java层代码,里面主要实现两个步骤,一个是定义native方法,另一个是调用System.loadLibrary()方法加载C层要写的动态库;

(2)Java代码使用javah命令生成JNI中所需的*.h头文件;

(3)实现以上所生成的*.h头文件;

(4)编写Android.mk文件,生存*.so

(5)编译整个工程生成apk

以下将以一个例程为例来进行说明,例程将展示从java调用native以及native调用java的方法,在此例程中,需要在native中实现的方法都是从java调用native的,onNativeCallback(int)这是从native调用java的,在以下的步骤中你将会见到它们.

以下将就这些步骤逐个讲解:

一:编写Java代码

根据我们需要实现的Java程序,来编写native方法和调用System.loadLibrary()方法加载动态库.

此例程中定义了4native方法,分别是:

1.nativeInitilize()native环境初始化

2.nativeThreadStart(String mstr):启动native线程

3.NativeThreadStop():停止native线程

4.onNativeCallback( int count )nativejava的回调

注意,在Java代码中的System.loadLibrary()中是不需要写完整so名称的,即如果你的so全程是:libnb.so,则在java代码中load该库应该使用System.loadLibrary(nb),因为lib前导在系统加载时会自动添加,否则就会出现错误,由上图可见,此处使用的so名称为:libnative.so.

Java文件的Android.mk文件如下:

二:用javah命令生成JNI中所需的.h头文件

javah命令是将Java源文件生成C/C++头文件,javah命令为:javah–jni包名&类名,并且执行该命令生成头文件需要在src路径下,在命令执行完成后,在当前目录就会发现所生成的.h头文件。

如下:

按照<>中定义的方法,此处生成的头文件内容为:

可以看到,需要native实现的有三个方法,并且他们的命名也有一定的规律,其实在使用JNI时,如果熟练的话可以略过此步,直接对应编写相应的C/C++实现即可。

三:实现.h头文件,编写c/c++实现

我们要根据前面所生成的.h文件编写c/c++文件来实现该头文件,.c.cpp文件的名称由用户自己定义,但在Android.mk文件中也要注意相应加入该文件到LOCAL_SRC_FILES,一般情况下,使用和.h相同的名字。

此处使用的文件名是:native.c

四:编写Android.mk文件,生成so动态库

当我们实现了.c.cpp文件后,编写Android.mk文件,用于将C/CPP生成动态库.

如果使用NDK开发则可使用[ndk-build]来进行编译。

五:编译与调试

但代码编译完成,即可进行单独或完整编译,以检测代码编写的正确性,若编译成功,则可以将编译生成的soapk导入到设备中或烧录所编译出来的完整版本即可进行调试。

本例程运行结果如下:


只要开发者遵守以上规则进行代码开发,出现问题的几率很小,也就是说jni的调试其实比较简单,所以本文就不再赘述。

5总结

以上主要介绍了JNI的一些基本知识以及使用例程,有了以上的基础,对JNI的使用应该已经没有问题了,以下列出一些在使用时容易出错的地方和使用建议。

5.1JNI使用注意事项

JNI虽然相对简单,但在使用时还是需要注意一些容易出现问题的地方:

  1. 错误检查

编写本地方法时应注意进行错误检查以在出现异常时快速发现问题

  1. JNI参数传递

JNI不会检查参数的正确性,所以在使用JNI时应保证参数的正确和有效性

  1. 勿混淆jclassjobject

初期使用jni时,容易将对象【jobject】引用和类【jclass】引用混淆.对象引用对应的是数据或者java.lang.Object及其子类的对象实例,而类引用对应的是java.lang.class的实例.

  1. Jboolean会存在数据截取问题

Jboolean是一个0-2558bitunsignedC类型,其中0JNI_FALSE,1-255对应JNI_TRUE.

5.2 JNI使用建议

针对JNI的使用有如下建议:

  1. 尽量使JAVAC的接口简单

  2. 尽量少写本地代码,并保持尽量独立



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值