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

Android JNI开发入门之二相关推荐

  1. SuperMap iMobile+Android studio开发入门(二)——超图示例代码运行

    背景:这里运行的是"产品入门"的"基于Android studio开发移动GIS程序"的"开发三维移动GIS程序",本篇对超图帮助文档进行了 ...

  2. Android JNI开发入门之一

    JNI在Android系统中有着广泛的应用.Android系统底层都是C/C++实现的,上层提供的API都是Java的,Java通过JNI调用底 层的实现.比如:Android API多媒体接口Med ...

  3. Android JNI开发入门与实战

    简介: 涉及到一些算法或者底层驱动的时候,往往需要使用jni来开发.现在官方推荐使用CMake工具来开发jni. 使用CMake开发Jni其实挺简单的,要求不高的话只需要简单配置一下就可以使用了. 配 ...

  4. 5G 时代的 Android App 开发入门与项目实战

    随着移动互联网的持续发展,Android系统从智能手机逐步拓展到平板电脑.智能电视.车载大屏.智能家居.智能手表等诸多设备,Android开发依然是前景可期的IT岗位. 当然,整个社会正在迈向5G时代 ...

  5. Android SELinux开发入门指南之正确姿势解决访问data目录权限问题

      Android SELinux开发入门指南之正确姿势解决访问data目录权限问题 Android SELinux开发多场景实战指南目录: Android SELinux开发入门指南之SELinux ...

  6. Android APP开发入门

    Android APP开发入门 目录 android_studio很好用的一个就是debug 1 1导入demo编译出错 1 4使用as运行安装不了apk安装adb 2 5SeekBar组件使用 2 ...

  7. AndroidStudio jni开发入门及打包so库和jar包

    AndroidStudio jni开发入门及打包so库和jar包 配置ndk开发环境 下载NDK,LLDB,CMake工具包 配置系统环境变量 在原有项目中进行jni开发环境配置 Androidstu ...

  8. Android JNI开发

    1. 在Android Studio开发JNI Android Studio第一个JNI开发入门(整理一)_sevenjoin的博客-CSDN博客_androidstudio jni开发 Androi ...

  9. 傻瓜式Android APP开发入门教程

    这篇文章主要介绍了Android APP开发入门教程,从SDK下载.开发环境搭建.代码编写.APP打包等步骤一一讲解,非常简明的一个Android APP开发入门教程,android各种机子和rom的 ...

最新文章

  1. oracle创建数据库总结,oracle创建数据库和用户方法总结
  2. Web容器自动对HTTP请求中参数进行URLDecode处理
  3. python 平滑曲线
  4. php大文件上传插件,PHP 大文件上传进度条实现
  5. Zookeeper数据的同步流程
  6. Kylin下构建Cube第一步出错:shell-init: error retrieving current directory
  7. Pycharm中设置py文件头部注释信息
  8. Java数据库表自动转化为PO对象
  9. Ubuntu赋予普通用户特定目录权限
  10. HTML控制表格边线显示。
  11. 解决linux服务器上matplotlib中文显示乱码问题
  12. MySQL之存储引擎,数据类型,约束条件
  13. springmvc执行原理(基于组件)
  14. 试读《线上幽灵:世界头号黑客米特尼克自传》
  15. 双硬盘双win10互不干扰_双硬盘装WIN7 WIN10双系统
  16. 〖小狼毫〗小狼毫使用心得分享
  17. 高中的班花貌美如花,我与一众兄弟将其“共享”了!
  18. conda.core.subdir_data.Response304ContentUnchanged CondaHTTPError: HTTP 000 CONNECTION FAILED
  19. 如何通过手机话费余额充值Q币?
  20. 【Virus Analysis】插入Unicode控制字符-RLO

热门文章

  1. JAVA 测试日期的不同显示格式
  2. python生成词云图个人技术报告_【Python成长之路】词云图制作
  3. 哪些情况下sql索引会失效
  4. Spark GraphX相关使用方法
  5. 中文linux最小,35M的中文linux硬盘简单安装方法Live-CD:SliTaz.tw-全世界最小的li
  6. linux命令封装sh,shell脚本学习之调用脚本将文件打包zip的方法示例
  7. php判断ipv6是否在范围内,[PHP] IPv6檢查IP是否在某個網段內 mtachcidr6
  8. java mybatis 搭建_mybatis环境搭建(转载)
  9. 微信公众号api关注接口php,微信公众平台接口开发入门示例
  10. linux性能诊断,linux下跟性能相关的命令以及系统性能诊断