使用Eclipse进行JNI开发

来源:互联网 发布:ss账号在淘宝上怎么买 编辑:程序博客网 时间:2024/05/17 00:53

一、概述

最近公司的项目里新增了调用硬件的算法的需求,这两天研究了一下JNI开发,也走了很多弯路,在此总结一下。

在进行开发之前,先确保以下工作已完成:

1、Android SDK以及NDK的安装和环境变量配置

      1.1首先我们先了解一下NDK:

      虽然对于大部分移动应用开发者来说可能都不会怎么接触NDK,但是如果涉及到硬件操作的话就不得不使用NDK了。使用NDK还有另外一个原因,就是C/C++更接触底层效率比较高,因此我们可以把一些耗时操作放在NDK中实现。

      NDK全称:Native Development Kit。

 1、NDK是一系列工具的集合。

 * NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk,这些工具对开发者的帮助是巨大的。

* NDK集成了交叉编译器,并提供了相应的mk文件隔离平台、CPU、API等差异,开发人员只需要简单修改mk文件      (指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。

* NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。

2、NDK提供了一份稳定、功能有限的API头文件声明。

Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。从该版本的NDK中看出,这些API      支持的功能非常有限,包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。

1.2 NDK环境变量配置

NDK下载地址:http://developer.android.com/ndk/downloads/index.html

下载完成后,解压到指定文件夹,再把NDK所在的目录路径添加到Path环境变量中,方便使用相关命令。



------------------------------------------完成以上准备工作,我们进入主题~------------------------------------------------------

首先我们了解一下JNI:

JNI是Java Native Interface的缩写,即Java本地调用。从Java1.1开始,JNI标准成为Java平台的一部分,它允许Java虚拟机(JVM)内部运行的Java代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。

    开发环境:Windows7、Eclipse


    JNI实现步骤:

   1、编写带有native声明的方法的java类。

   2、使用javah+java类全类名生成.h文件。

   3、使用C/C++实现本地方法。

   4、将C/C++编写的文件生成动态连接库。

   5、编写Java调用代码、进行测试。

   下面我们按照上述步骤进行开发:

   1、编写带有native声明的方法的java类。

    创建Android Project:TestJNI,新建带有native声明的方法的TestJNI类,添加加载c库的代码,其中“testJNI”为so的文件名

static{System.loadLibrary("testJNI");}

   声明动态库中需要自定义的原生函数:

public native String getString();public native int getInt();

    2、使用javah+java全类名生成.h文件。

    我们都知道在Eclipse中新建类后,Eclipse会帮助我们在项目下自动建立去对应的class文件,位于项目目录下的\bin\classes中。头文件可以根据.class文件通过javah编译生成。

进入到工程下bin\classes目录下,执行cmd,输入:javah com.example.testjni.TestJNI


    就会在该目录下生成头文件即com_example_testjni_TestJNI.h,头文件中定义了与上层的访问接口Java_com_example_testjni_TestJNI_getString以及Java_com_example_testjni_TestJNI_getInt,方法名是按照”Java_包名_类名_方法名“来命名的,如下图:



     3、使用C/C++实现本地方法。

           3.1、处理JNI

           右击Project->Android Tools->Add Native Support,这里注意一点,如果没有添加NDK路径的话,就会无法到下面这一步,所以如果弹出的框出现这种提示:


我们进行一下添加NDK路径操作:Window->Preferences->Android->NDK,选择NDK所在的目录,点击OK即可。

添加好了NDK的路径,在此进行我们之前的操作,在弹出的框里输入so的文件名,也就是在TestJNI类中写的so文件名,注意这里要统一!切记!



完成后工程自动生成jni文件夹,jni文件夹下自动生成了testJNI.cpp以及Android.mk,如下图:


    3.3、将.h文件复制到jni目录下

    此时可能会出现如下报错:

Type '*****' could not be resolved
   这是由于jni.h没有导入,解决办法:
   Project Properties -> C/C++ General -> Path and Symbols选择include标签

   Add ->$Android_NDK_HOME/platforms/android-14/arch-arm/usr/include即可。

    3.3、编写C/C++(这里为C++代码)

#include <jni.h>extern "C"JNIEXPORT jstring JNICALL Java_com_example_testjni_TestJNI_getString  (JNIEnv *env, jobject obj){return env->NewStringUTF("HelloJNI!");}JNIEXPORT jint JNICALL Java_com_example_testjni_TestJNI_getInt  (JNIEnv *env, jobject obj){return 1;}

JNIEXPORT、JNICALL 、JNIEnv和jobject都是JNI标准中所定义的类型或者宏,它们的含义如下:

JNIEXPORT和JNICALL:他们是JNI中所定义的宏,可以在jni.h这个头文件中查询到;

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

jobject:表示Java对象中的this;

   注意:JNI调用C或者C++存在区别:

   1、JNI是由C语言定义接口的,JNI通过函数名找函数入口,执行函数里的内容。这和函数用什么语言生成的并没有关系。只要保证函数名称符合JNI的协议。而使用C++要注意的是C++默认生成的函数名称和你写在源文件中的名称并不相同,因为C++要处理函数重载,会在函数名称中加上参数信息,这称为name mangling。解决方法是定义函数时在前面加上extern "C"修饰,告诉编译器这函数要被C调用(当然,其实是JNI)。本质没有什么区别,一个是JNI调C,一个是JNI调C,C再掉C++,这样理解就简单了~

  2、在C的定义中,env是一个两级指针,而在C++的定义中,env是个一级指针
C形式需要对env指针进行双重deferencing,而且须将env作为第一个参数传给jni函数,举个简单的例子:


对于XXX.c:JNI 函数调用由“(*env)->”作前缀,目的是为了取出函数指针所引用的值。

return (*env)->NewStringUTF(env,"HelloJNI!");

对于XXX.cpp:JNIEnv 类拥有处理函数指针查找的内联成员函数。

return env->NewStringUTF("HelloJNI!");

可以发现,C或者C++的实现很类似,但是它们对env的操作方式有所不同,因此使用C和C++来实现同一个JNI方法,它们的区别主要集中在对env的操作上,其他都是类似的。   

4、将C/C++编写的文件生成动态连接库

            4.1、编写Android.mk文件以及Application.mk

            Android.mk 文件是Android 的 makefile文件,下面逐句解释

       LOCAL_PATH - 编译时的目录
       $(call 目录,目录….) 目录引入操作符
    如该目录下有个文件夹名称 src,则可以这样写 $(call src),那么就会得到 src 目录的完整路径
       include $(CLEAR_VARS) -清除之前的一些系统变量
       LOCAL_MODULE - 编译生成的目标对象即so的文件名
       LOCAL_SRC_FILES - 编译的源文件,C/C++文件
       LOCAL_C_INCLUDES - 需要包含的头文件目录
       LOCAL_SHARED_LIBRARIES - 链接时需要的外部库
       LOCAL_PRELINK_MODULE - 是否需要prelink处理 
       include$(BUILD_SHARED_LIBRARY) - 指明要编译成动态库
     如果想在多个平台下生成so文件,还需要添加Application.mk文件,如果没有该文件,则只会生成armeabi目录下的so文件。

     在jni目录下新建Application.mk文件,该文件内容很简单:

APP_ABI := all

即可在多平台下生成so文件。

           4.2、使用NDK编译生成so库

           进入到jni目录下,执行cmd,输入:

ndk-build

          如下图:


刷新Project,在libs目录下,生成多平台下的so文件,如下图:


     5、编写Java调用代码、进行测试。

TestJNI testJNI=new TestJNI();Log.e("TAG", testJNI.getString());

运行起来,打印出log信息,如下图:



至此就完成了使用Eclipse进行JNI开发~下篇将讲述使用Android Studio进行JNI开始,原理都差不多啦~吐舌头








0 0
原创粉丝点击