在上一篇文章《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. #defineLOG_TAG"HelloWorld"
  3. #include<utils/Log.h>
  4. /*
  5. *Class:com_simon_Helloworld
  6. *Method:print
  7. *Signature:()V
  8. */
  9. /*JNIEXPORTvoidJNICALLJava_com_simon_Helloworld_print(JNIEnv*,jobject)*/
  10. JNIEXPORTjstringJNICALLJava_com_simon_HelloWorld_printJNI(JNIEnv*env,jobjectobj)
  11. {
  12. LOGI("HelloWorldFromlibhelloworld.so!");
  13. returnenv->NewStringUTF("HelloWorld!");
  14. }
  15. staticconstchar*classPathName="com/simon/HelloWorld";
  16. staticJNINativeMethodmethods[]={
  17. {"printJNI","()Ljava/lang/String;",(void*)Java_com_simon_HelloWorld_printJNI},
  18. };
  19. /*
  20. *Registerseveralnativemethodsforoneclass.
  21. */
  22. staticintregisterNativeMethods(JNIEnv*env,constchar*className,
  23. JNINativeMethod*gMethods,intnumMethods)
  24. {
  25. jclassclazz;
  26. clazz=env->FindClass(className);
  27. if(clazz==NULL){
  28. LOGE("Nativeregistrationunabletofindclass'%s'",className);
  29. returnJNI_FALSE;
  30. }
  31. if(env->RegisterNatives(clazz,gMethods,numMethods)<0){
  32. LOGE("RegisterNativesfailedfor'%s'",className);
  33. returnJNI_FALSE;
  34. }
  35. returnJNI_TRUE;
  36. }
  37. /*
  38. *Registernativemethodsforallclassesweknowabout.
  39. *
  40. *returnsJNI_TRUEonsuccess.
  41. */
  42. staticintregisterNatives(JNIEnv*env)
  43. {
  44. if(!registerNativeMethods(env,classPathName,
  45. methods,sizeof(methods)/sizeof(methods[0]))){
  46. returnJNI_FALSE;
  47. }
  48. returnJNI_TRUE;
  49. }
  50. typedefunion{
  51. JNIEnv*env;
  52. void*venv;
  53. }UnionJNIEnvToVoid;
  54. /*Thisfunctionwillbecallwhenthelibraryfirstbeloaded*/
  55. jintJNI_OnLoad(JavaVM*vm,void*reserved)
  56. {
  57. UnionJNIEnvToVoiduenv;
  58. JNIEnv*env=NULL;
  59. LOGI("JNI_OnLoad!");
  60. if(vm->GetEnv((void**)&uenv.venv,JNI_VERSION_1_4)!=JNI_OK){
  61. LOGE("ERROR:GetEnvfailed");
  62. return-1;
  63. }
  64. env=uenv.env;;
  65. if(registerNatives(env)!=JNI_TRUE){
  66. LOGE("ERROR:registerNativesfailed");
  67. return-1;
  68. }
  69. returnJNI_VERSION_1_4;
  70. }

本例与上文《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:=$(callmy-dir)
  2. include$(CLEAR_VARS)
  3. LOCAL_SRC_FILES:=com_simon_Helloworld.cpp
  4. LOCAL_C_INCLUDES:=$(JNI_H_INCLUDE)
  5. LOCAL_MODULE:=libhelloworld
  6. LOCAL_SHARED_LIBRARIES:=libutils
  7. LOCAL_PRELINK_MODULE:=false
  8. 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


更多相关文章

  1. C语言函数的递归(上)
  2. Android(安卓)5.1 SurfaceFlinger VSYNC详解
  3. Android网络状态实时监听实例代码(二)
  4. linux kernel suspend Resume
  5. 关于Sytem.gc()主动触发Android(安卓)GC
  6. Android(安卓)客户端与服务器端进行数据交互(二、登录客户端)
  7. Android(安卓)Says Bonjour
  8. android编写Service入门用法与教程
  9. Android(安卓)suspend底层流程解析

随机推荐

  1. 成为更好的Android开发者的30多个技巧
  2. Android基础和运行机制
  3. Android(安卓)Handler
  4. Android中LOG机制详解(上)
  5. Android,开源还是封闭?
  6. Android:Touch和Click的区别 编辑
  7. Xamarin For Visual Studio 3.0.54.0 完
  8. Android(安卓)编译时View注入工具的实现
  9. Android高手进阶教程(二十三)之---Androi
  10. 基于android的远程视频监控系统(已开放源