织梦CMS - 轻松建站从此开始!

罗索

当前位置: 主页>嵌入式开发>Android>

Android JNI开发入门之二

落鹤生 发布于 2012-04-11 13:41 点击:次 
在上一篇文章《Android JNI开发入门之一》中,我介绍了Android应用程序(APK)怎样通过JNI调用Native C实现的共享库。本文将进一步介绍Android应用程序通过JNI调用Native C++实现的共享库,并实现一个和上文《Android JNI开发入门之一》相同功能的Helloworld应用程序。
TAG:

在上一篇文章《Android JNI开发入门之一》中,我介绍了Android应用程序(APK)怎样通过JNI调用Native C实现的共享库。本文将进一步介绍Android应用程序通过JNI调用Native C++实现的共享库,并实现一个和上文《Android JNI开发入门之一》相同功能的Helloworld应用程序。

两套不同的API

    前文已经提到,Android系统的Java虚拟机为C和C++实现两套不同的API,所以我们调用的时候需要注意这一点儿。另外Google并没有提供 JNI的文档,我们调用的时候可以参考Android的jni.h文件,里面有C和C++的JNI函数原型。也可以把本例的相同功能HelloWorld 库和上文《Android JNI开发入门之一》进行比较。

C++实现HelloWorld共享库

    在本例中Android应用程序不需要有任何变化,我们需要重新用C++实现HelloWorld共享库。创建com_simon_Helloworld.cpp文件,并在文件中输入如下内容:

  1. #include <jni.h> 
  2. #define LOG_TAG "HelloWorld" 
  3. #include <utils/Log.h> 
  4.  
  5.  /* 
  6.   * Class:     com_simon_Helloworld 
  7.   * Method:    print 
  8.   * Signature: ()V 
  9.   */ 
  10. /*JNIEXPORT void JNICALL Java_com_simon_Helloworld_print(JNIEnv *, jobject)*/ 
  11. JNIEXPORT jstring JNICALL Java_com_simon_HelloWorld_printJNI(JNIEnv *env, jobject obj) 
  12.     LOGI("Hello World From libhelloworld.so!"); 
  13.     return env->NewStringUTF("Hello World!"); 
  14.  
  15. static const char *classPathName = "com/simon/HelloWorld"
  16.  
  17. static JNINativeMethod methods[] = { 
  18.   {"printJNI""()Ljava/lang/String;", (void*)Java_com_simon_HelloWorld_printJNI }, 
  19. }; 
  20.  
  21. /* 
  22.  * Register several native methods for one class. 
  23.  */ 
  24. static int registerNativeMethods(JNIEnv* env, const char* className, 
  25.     JNINativeMethod* gMethods, int numMethods) 
  26.     jclass clazz; 
  27.  
  28.     clazz = env->FindClass(className); 
  29.     if (clazz == NULL) { 
  30.         LOGE("Native registration unable to find class '%s'", className); 
  31.         return JNI_FALSE; 
  32.     } 
  33.     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { 
  34.         LOGE("RegisterNatives failed for '%s'", className); 
  35.         return JNI_FALSE; 
  36.     } 
  37.  
  38.     return JNI_TRUE; 
  39.  
  40. /* 
  41.  * Register native methods for all classes we know about. 
  42.  * 
  43.  * returns JNI_TRUE on success. 
  44.  */ 
  45. static int registerNatives(JNIEnv* env) 
  46.   if (!registerNativeMethods(env, classPathName, 
  47.                  methods, sizeof(methods) / sizeof(methods[0]))) { 
  48.     return JNI_FALSE; 
  49.   } 
  50.  
  51.   return JNI_TRUE; 
  52.  
  53. typedef union { 
  54.     JNIEnv* env; 
  55.     void* venv; 
  56. } UnionJNIEnvToVoid; 
  57.  
  58. /* This function will be call when the library first be loaded */ 
  59. jint JNI_OnLoad(JavaVM* vm, void* reserved) 
  60.     UnionJNIEnvToVoid uenv; 
  61.     JNIEnv* env = NULL; 
  62.     LOGI("JNI_OnLoad!"); 
  63.  
  64.     if (vm->GetEnv((void**)&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { 
  65.         LOGE("ERROR: GetEnv failed"); 
  66.         return -1; 
  67.     } 
  68.  
  69.     env = uenv.env;; 
  70.  
  71.     if (registerNatives(env) != JNI_TRUE) { 
  72.         LOGE("ERROR: registerNatives failed"); 
  73.         return -1; 
  74.     } 
  75.  
  76.      return JNI_VERSION_1_4; 

本例与上文《Android JNI开发入门之一》对比有如下几点不同需要注意:

1、C和C++实现共享库调用不同JNI API。前面已经提到Android系统JNI为C和C++提供了两套不同的API。请仔细对比NewStringUTF,GetEnv函数,就会发现JNI API不同。

2、C++版的helloworld共享库提供了函数映射表。前文《Android JNI开发入门之一》也已经提到,JNI API为了避免丑陋的函数名,提供了方法向Java虚拟机注册函数映射表。这样当Java调用Native接口的时候,Java虚拟机就可以不用根据函数名来决定调用哪个函数了,直接通过查询表格就可以找到需要调用的函数了。

3、我们注意到RegisterNatives第一个参数(C语言接口中是第二个参数)为调用该函数的Java类。这也和标准JNI函数名包含类名(包名和类名)的作用一样——声明那个Java类可以调用这个方法。

4、函数映射表的定义非常的怪异。你可以参考Android JNI 使用的数据结构JNINativeMethod详解JNI标准手册相关类型的部分

    通过对比你会发现C++的实现同样功能的共享库比C加入更多的代码,另外你可能会有疑问既然Java虚拟机能用通过函数名访问到相应的Native code函数,为什么还要提供注册映射函数表呢?没错!作为一个HelloWorld程序,确实简单为第一要务!如果Java虚拟机能用函数名能访问到相 应的函数的话,我是不会多此一举来注册映射函数表。在实践中我发现:

   标准JNI不能通过标准函数名找到C++实现的Helloworld共享库中的函数,但是C实现的helloworld共享没有这个问题。我不知道为什么会这样,请达人指教。没有办法才提供注册映射函数表。

    下面提供一个helloworld共享库的Makefile——Android.mk:

  1. LOCAL_PATH:= $(call my-dir) 
  2. include $(CLEAR_VARS) 
  3.  
  4. LOCAL_SRC_FILES:=com_simon_Helloworld.cpp 
  5.  
  6. LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) 
  7.  
  8. LOCAL_MODULE := libhelloworld 
  9.  
  10. LOCAL_SHARED_LIBRARIES := libutils 
  11.  
  12. LOCAL_PRELINK_MODULE := false 
  13.  
  14. include $(BUILD_SHARED_LIBRARY) 

    和前文《Android JNI开发入门之一》的Android.mk文件相比也就是修改了一下源文件,没有什么可说。编译生成libhelloworld.so文件,允许前文HelloWorld Android应用程序,你将会得到和前文相同的结果。

JNI的进一步学习

    通过上面的例子我们已经初步掌握了Android编写JNI程序的方法。这也只能算是Android JNI入门而已,有很多JNI相关的内容我们在例子中并没有涉及到,比如:

1、我们并没有提到怎样在Native代码中回调Java的函数。

2、每个Native的函数中前两个参数是什么意思?

    如果想用JNI进行Android应用开发我们需要更深入的学习。为了大家共同进步,我这里也可以提供一些相关的资料和方法。

    如果我们想深入学习JNI首先要先熟悉标准的JNI。推荐大家学习《Java Native Interface: Programmer’s Guide and Specification》,权威的标准JNI学习文档。

    前文提到Google没有Android JNI编程提供文档,但是在网上Simon也找到了一篇非常好的文档供大家参考——JNI Examples for Android。这篇文章中举了一个例子包含了Android JNI开发的方方面面,想深入学习Android JNI开发的朋友请仔细研读。

总结

    俗话说得好“问道有先后,如是而已”,Simon也是刚刚才开始学习Android JNI编程,对JNI的理解上面难免有一些地方有错误,欢迎朋友们指正。

参考资料:

Android JNI 使用的数据结构JNINativeMethod详解

Android JNI实例

JNI Examples for Android

How to add a new module to Android

Android JNI(实现自己的JNI_OnLoad函数)

Android中JNI编程的那些事儿

Java Native Interface: Programmer’s Guide and Specification

Java Native Interface Specification

(Simon)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201204/15925.html]
本文出处:unix-center.net 作者:Simon 原文
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容