android jni开发之(二)--------.h/.c/.so文件的生成和调用

 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的文件名

[java]  view plain  copy
  1. static{  
  2.         System.loadLibrary("testJNI");  
  3.     }  

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

[java]  view plain  copy
  1. public native String getString();  
  2. 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目录下

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

[java]  view plain  copy
  1. 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++代码)

[cpp]  view plain  copy
  1. #include <jni.h>  
  2.   
  3. extern "C"  
  4. JNIEXPORT jstring JNICALL Java_com_example_testjni_TestJNI_getString  
  5.   (JNIEnv *env, jobject obj){  
  6.     return env->NewStringUTF("HelloJNI!");  
  7.   
  8. }  
  9. JNIEXPORT jint JNICALL Java_com_example_testjni_TestJNI_getInt  
  10.   (JNIEnv *env, jobject obj){  
  11.   
  12.     return 1;  
  13.   
  14. }  

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)->”作前缀,目的是为了取出函数指针所引用的值。

[cpp]  view plain  copy
  1. return (*env)->NewStringUTF(env,"HelloJNI!");  

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

[cpp]  view plain  copy
  1. 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文件,该文件内容很简单:

[plain]  view plain  copy
  1. APP_ABI := all  

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

           4.2、使用NDK编译生成so库

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

[plain]  view plain  copy
  1. ndk-build  

          如下图:


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


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

[java]  view plain  copy
  1. TestJNI testJNI=new TestJNI();  
  2. Log.e("TAG", testJNI.getString());  

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

猜你喜欢

转载自blog.csdn.net/u011753609/article/details/80037380