原文网址:http://blog.csdn.net/conowen/article/details/7521340

********************************************************************************************
 * author:conowen@大钟                                                                                                                          
 * E-mail:conowen@hotmail.com

*site:http://menwoo.com/

*深圳市大望谷科技有限公司                                                                                                        
 * http://blog.csdn.net/conowen                                                                                                             
 * 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。

********************************************************************************************/

1、JNI简介

JNI全称为Java Native Interface(JAVA本地调用)。从Java1.1开始,JNI成为java平台的一部分,它允许Java代码和其他语言写的代码(如C&C++)进行交互。并非从Android发布才引入JNI的概念的。

2、JNI与NDK

简单来说,Android的NDK提供了一些交叉编译工具链和Android自带的库,这些Android的库可以让开发者在编写本地语言的程序时调用。而NDK提供的交叉编译工具链就对已经编写好的C&C++代码进行编译,生成库。

当然了,你也可以自己搭建交叉编译环境,而不用NDK的工具和库。然后生成库,只要规范操作,一样可以生成能让JAVA层成功调用的库文件的。

利用NDK进行编译本地语言可以参考这篇博文:http://blog.csdn.net/conowen/article/details/7522667

3、JNI  调用流程

众所周知,Android的应用层的类都是以Java写的,这些Java类编译为Dex文件之后,必须靠Dalvik虚拟机( Virtual Machine)来执行。假如在执行java程序时,需要载入C&C++函数时,Dalvik虚拟机就会去加载C&C++的库,(System.loadLibrary("libName");)让java层能顺利地调用这些本地函数。需要清楚一点,这些C&C++的函数并不是在Dalvik虚拟机中运行的,所以效率和速度要比在Dalvik虚拟机中运行得快很多。

Dalvik虚拟机成功加载库之后,就会自动地寻找库里面的JNI_OnLoad函数,这个函数用途如下:

(1)告诉Dalvik虚拟机此C库使用哪一个JNI版本。如果你的库里面没有写明JNI_OnLoad()函数,VM会默认该库使用最老的JNI 1.1版本。但是新版的JNI做了很多的扩充,也优化了一些内容,如果需要使用JNI的新版功能,就必须在JNI_OnLoad()函数声明JNI的版本。如

  1. result = JNI_VERSION_1_4;

当没有JNI_OnLoad()函数时,Android调试信息会做出如下提示(No JNI_OnLoad found)

  1. 04-29 13:53:12.184: D/dalvikvm(361): Trying to load lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98
  2. 04-29 13:53:12.204: D/dalvikvm(361): Added shared lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98
  3. 04-29 13:53:12.204: D/dalvikvm(361): No JNI_OnLoad found in /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea98, skipping init

(2)因为Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数,所以我们可以在JNI_OnLoad()里面进行一些初始化工作,如注册JNI函数等等。注册本地函数,可以加快java层调用本地函数的效率。

另外:与JNI_OnLoad()函数相对应的有JNI_OnUnload()函数,当虚拟机释放该C库时,则会调用JNI_OnUnload()函数来进行善后清除动作。

4、例子(关于jni里面的数据类型转换与常用jni方法下一篇博文介绍)

下面以havlenapetr的FFmpeg工程里面的onLoad.cpp为例详细说一下:

  1. //onLoad.cpp文件
  2. #define TAG "ffmpeg_onLoad"
  3. #include <stdlib.h>
  4. #include <android/log.h>
  5. #include "jniUtils.h"
  6. extern "C" {
  7. extern int register_android_media_FFMpegAVRational(JNIEnv *env);
  8. #ifdef BUILD_WITH_CONVERTOR
  9. extern int register_android_media_FFMpeg(JNIEnv *env);
  10. #endif
  11. extern int register_android_media_FFMpegAVFormatContext(JNIEnv *env);
  12. extern int register_android_media_FFMpegAVInputFormat(JNIEnv *env);
  13. }
  14. extern int register_android_media_FFMpegAVCodecContext(JNIEnv *env);
  15. extern int register_android_media_FFMpegUtils(JNIEnv *env);
  16. extern int register_android_media_FFMpegAVFrame(JNIEnv *env);
  17. #ifdef BUILD_WITH_PLAYER
  18. extern int register_android_media_FFMpegPlayerAndroid(JNIEnv *env);
  19. #endif
  20. static JavaVM *sVm;
  21. /*
  22. * Throw an exception with the specified class and an optional message.
  23. */
  24. int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
  25. jclass exceptionClass = env->FindClass(className);
  26. if (exceptionClass == NULL) {
  27. __android_log_print(ANDROID_LOG_ERROR,
  28. TAG,
  29. "Unable to find exception class %s",
  30. className);
  31. return -1;
  32. }
  33. if (env->ThrowNew(exceptionClass, msg) != JNI_OK) {
  34. __android_log_print(ANDROID_LOG_ERROR,
  35. TAG,
  36. "Failed throwing '%s' '%s'",
  37. className, msg);
  38. }
  39. return 0;
  40. }
  41. JNIEnv* getJNIEnv() {
  42. JNIEnv* env = NULL;
  43. if (sVm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
  44. __android_log_print(ANDROID_LOG_ERROR,
  45. TAG,
  46. "Failed to obtain JNIEnv");
  47. return NULL;
  48. }
  49. return env;
  50. }
  51. /*
  52. * Register native JNI-callable methods.
  53. *
  54. * "className" looks like "java/lang/String".
  55. */
  56. int jniRegisterNativeMethods(JNIEnv* env,
  57. const char* className,
  58. const JNINativeMethod* gMethods,
  59. int numMethods)
  60. /*从com_media_ffmpeg_FFMpegPlayer.cpp文件跳到此,完成最后的注册
  61. * 向 Dalvik虚拟机(即AndroidRuntime)登记传过来的参数gMethods[]所含的本地函数
  62. */
  63. {
  64. jclass clazz;
  65. __android_log_print(ANDROID_LOG_INFO, TAG, "Registering %s natives\n", className);
  66. clazz = env->FindClass(className);
  67. if (clazz == NULL) {
  68. __android_log_print(ANDROID_LOG_ERROR, TAG, "Native registration unable to find class '%s'\n", className);
  69. return -1;
  70. }
  71. if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
  72. __android_log_print(ANDROID_LOG_ERROR, TAG, "RegisterNatives failed for '%s'\n", className);
  73. return -1;
  74. }
  75. return 0;
  76. }
  77. //Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数
  78. jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  79. JNIEnv* env = NULL;//定义JNI Env
  80. jint result = JNI_ERR;
  81. sVm = vm;
  82. /*JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint);
  83. * GetEnv()函数返回的  Jni 环境对每个线程来说是不同的,
  84. *  由于Dalvik虚拟机通常是Multi-threading的。每一个线程调用JNI_OnLoad()时,
  85. *  所用的JNI Env是不同的,因此我们必须在每次进入函数时都要通过vm->GetEnv重新获取
  86. *
  87. */
  88. //得到JNI Env
  89. if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
  90. __android_log_print(ANDROID_LOG_ERROR, TAG, "GetEnv failed!");
  91. return result;
  92. }
  93. __android_log_print(ANDROID_LOG_INFO, TAG, "loading . . .");
  94. /*开始注册
  95. * 传入参数是JNI env
  96. * 由于下面有很多class,只以register_android_media_FFMpegPlayerAndroid(env)为例子
  97. */
  98. #ifdef BUILD_WITH_CONVERTOR
  99. if(register_android_media_FFMpeg(env) != JNI_OK) {
  100. __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpeg");
  101. goto end;
  102. }
  103. #endif
  104. if(register_android_media_FFMpegAVFormatContext(env) != JNI_OK) {
  105. __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVFormatContext");
  106. goto end;
  107. }
  108. if(register_android_media_FFMpegAVCodecContext(env) != JNI_OK) {
  109. __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVCodecContext");
  110. goto end;
  111. }
  112. if(register_android_media_FFMpegAVRational(env) != JNI_OK) {
  113. __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVRational");
  114. goto end;
  115. }
  116. if(register_android_media_FFMpegAVInputFormat(env) != JNI_OK) {
  117. __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVInputFormat");
  118. goto end;
  119. }
  120. if(register_android_media_FFMpegUtils(env) != JNI_OK) {
  121. __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegUtils");
  122. goto end;
  123. }
  124. if(register_android_media_FFMpegAVFrame(env) != JNI_OK) {
  125. __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVFrame");
  126. goto end;
  127. }
  128. #ifdef BUILD_WITH_PLAYER
  129. if(register_android_media_FFMpegPlayerAndroid(env) != JNI_OK) {//跳到----》com_media_ffmpeg_FFMpegPlayer.cpp文件
  130. __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegPlayerAndroid");
  131. goto end;
  132. }
  133. #endif
  134. __android_log_print(ANDROID_LOG_INFO, TAG, "loaded");
  135. result = JNI_VERSION_1_4;
  136. end:
  137. return result;
  138. }
    1. //com_media_ffmpeg_FFMpegPlayer.cpp文件
    2. /*
    3. *
    4. * 由于代码量较大,com_media_ffmpeg_FFMpegPlayer.cpp开始的一部分省略,只是贴出注册函数的相关部分。
    5. */
    6. static const char* const kClassPathName = "com/media/ffmpeg/FFMpegPlayer";
    7. /*
    8. * 由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,
    9. * 可多次调用registerNativeMethods()函数来更换本地函数的指针,
    10. * 从而达到弹性调用本地函数的目的。
    11. */
    12. static JNINativeMethod gMethods[] = {
    13. {"setDataSource",       "(Ljava/lang/String;)V",            (void *)com_media_ffmpeg_FFMpegPlayer_setDataSource},
    14. {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)com_media_ffmpeg_FFMpegPlayer_setVideoSurface},
    15. {"prepare",             "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_prepare},
    16. {"_start",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_start},
    17. {"_stop",               "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_stop},
    18. {"getVideoWidth",       "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getVideoWidth},
    19. {"getVideoHeight",      "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getVideoHeight},
    20. {"seekTo",              "(I)V",                             (void *)com_media_ffmpeg_FFMpegPlayer_seekTo},
    21. {"_pause",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_pause},
    22. {"isPlaying",           "()Z",                              (void *)com_media_ffmpeg_FFMpegPlayer_isPlaying},
    23. {"getCurrentPosition",  "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getCurrentPosition},
    24. {"getDuration",         "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getDuration},
    25. {"_release",            "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_release},
    26. {"_reset",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_reset},
    27. {"setAudioStreamType",  "(I)V",                             (void *)com_media_ffmpeg_FFMpegPlayer_setAudioStreamType},
    28. {"native_init",         "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_native_init},
    29. {"native_setup",        "(Ljava/lang/Object;)V",            (void *)com_media_ffmpeg_FFMpegPlayer_native_setup},
    30. {"native_finalize",     "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_native_finalize},
    31. {"native_suspend_resume", "(Z)I",                           (void *)com_media_ffmpeg_FFMpegPlayer_native_suspend_resume},
    32. };
    33. int register_android_media_FFMpegPlayerAndroid(JNIEnv *env) {
    34. return jniRegisterNativeMethods(env, kClassPathName, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));
    35. /*跳到OnLoad.cpp文件中的
    36. * jint jniRegisterNativeMethods(JNIEnv* env,
    37. const char* className,
    38. const JNINativeMethod* gMethods,
    39. int numMethods)
    40. */
    41. }

【转】 Android的NDK开发(1)————Android JNI简介与调用流程的更多相关文章

  1. Android的NDK开发(1)————Android JNI简介与调用流程

    1.JNI简介 JNI全称为Java Native Interface(Java本地调用).从Java1.1开始,JNI成为java平台的一部分,它允许Java代码和其他语言写的代码(如C&C ...

  2. Android的NDK开发(5)————Android JNI层实现文件的read、write与seek操作

    1. 在Android的Java层实现文件的读写操作是非常简单的,可以参看之前写的博文:http://blog.csdn.net/conowen/article/details/7296121 在JN ...

  3. Android之NDK开发(转)

    Android之NDK开发 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第 ...

  4. Android之NDK开发

    转自:http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支持C ...

  5. 【转】Android之NDK开发

    原文网址:http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支 ...

  6. Android之NDK开发(转载)

    http://www.cnblogs.com/devinzhang/archive/2012/02/29/2373729.html 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C+ ...

  7. 【Android】Android Studio NDK 开发

    Android Studio NDK 开发 记录在Android Studio中NDK简单开发的步骤 用到的Android Studio版本为3.5. 配置NDK 下载NDK 一般在SDK下已经有自带 ...

  8. Android下NDK开发环境搭建

    Android下NDK开发环境搭建 1.     AndroidNDK安装与配置 1.1  NDK简介 Android NDK是一套允许开发人员使用本地代码(如C/C++)进行Android APP部 ...

  9. Android Studio NDK开发-JNI调用Java方法

    相对于NDK来说SDK里面有更多API可以调用,有时候我们在做NDK开发的时候,需要在JNI直接Java中的方法和变量,比如callback,系统信息等.... 如何在JNI中调用Java方法呢?就需 ...

随机推荐

  1. Windows开启Telnet

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/4301513.html ...

  2. My way to Python - Day05 - 面向对象-思维导图

    My way to Python - Day05 - 面向对象   思维导图

  3. AS【常用插件】

    安装插件,Settings -->[Plugins]-->搜索-->点击install-->重启AS 禁用插件,右侧面板会显示出已经安装的插件列表,取消勾选即可禁用插件 AS插 ...

  4. Java-strurs总结

    这里是自己对自学的struts2 的一个整体 的脉络进行的一个概括,需要学习哪些东西,注重哪些东西: struts2 是主流框架SSH 中的一个"S" ,准备MVC开发标准的一个框 ...

  5. u盘复制提示文件过大

    应该有很多个朋友也遇到过同样的问题,就是我们的u盘的明明可用的空间还有很多,甚至一个空的16g的u盘,但从window等操作系统向u盘拷贝文件的时候,却不能容下诸如iso4g的镜像文件,难道是生产u盘 ...

  6. ZOJ2112 Dynamic Rankings 动态区间第K最值 平方分割

    有了上一题的经验(POJ的静态区间第K最值)再解决这道题就轻松多了 空间5256KB,时间3330ms,如果把动态开点的平衡树换成数组模拟的话应该会更快 之所以选择了平方分割而不是树套树,不仅是所谓趁 ...

  7. elastic search 学习笔记

    Elastic search在数据分析的应用中相当于一个数据库的搜索引擎. 跟MySQL类似,它有自己的查询语言,只不过不是关系型数据库,属于NoSQL. 可以根据索引从分布式服务器文件系统中快速存取 ...

  8. hold

    嘿嘿,很久没写博客了.一懒一拖一浮躁就不行了. 果然烦心事太多,一直懒得编程.结果还是编程才能平复我啊! 明天那什么,别担心,平常心嘛! 还好,看了几部电影,不算没收获.自己有意思就看看电影,别瞎想啥 ...

  9. zoj3839-Poker Face

    #include<cstdio>int n;void P(int i,int j,int n,int f){ if(i==n){ for(int k=1;k<=n;k++)print ...

  10. js时间戳与日期格式之间的互转

    1. 将时间戳转换成日期格式 // 简单的一句代码 var date = new Date(时间戳); //获取一个时间对象 注意:如果是uinx时间戳记得乘于1000.比如php函数time()获得 ...