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的版本。如

[java] view plain copy
  1. result=JNI_VERSION_1_4;

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

[java] view plain copy
  1. 04-2913:53:12.184:D/dalvikvm(361):Tryingtoloadlib/data/data/com.conowen.helloworld/lib/libHelloWorld.so0x44edea98
  2. 04-2913:53:12.204:D/dalvikvm(361):Addedsharedlib/data/data/com.conowen.helloworld/lib/libHelloWorld.so0x44edea98
  3. 04-2913:53:12.204:D/dalvikvm(361):NoJNI_OnLoadfoundin/data/data/com.conowen.helloworld/lib/libHelloWorld.so0x44edea98,skippinginit


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


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



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

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

[cpp] view plain copy
  1. //onLoad.cpp文件
  2. #defineTAG"ffmpeg_onLoad"
  3. #include<stdlib.h>
  4. #include<android/log.h>
  5. #include"jniUtils.h"
  6. extern"C"{
  7. externintregister_android_media_FFMpegAVRational(JNIEnv*env);
  8. #ifdefBUILD_WITH_CONVERTOR
  9. externintregister_android_media_FFMpeg(JNIEnv*env);
  10. #endif
  11. externintregister_android_media_FFMpegAVFormatContext(JNIEnv*env);
  12. externintregister_android_media_FFMpegAVInputFormat(JNIEnv*env);
  13. }
  14. externintregister_android_media_FFMpegAVCodecContext(JNIEnv*env);
  15. externintregister_android_media_FFMpegUtils(JNIEnv*env);
  16. externintregister_android_media_FFMpegAVFrame(JNIEnv*env);
  17. #ifdefBUILD_WITH_PLAYER
  18. externintregister_android_media_FFMpegPlayerAndroid(JNIEnv*env);
  19. #endif
  20. staticJavaVM*sVm;
  21. /*
  22. *Throwanexceptionwiththespecifiedclassandanoptionalmessage.
  23. */
  24. intjniThrowException(JNIEnv*env,constchar*className,constchar*msg){
  25. jclassexceptionClass=env->FindClass(className);
  26. if(exceptionClass==NULL){
  27. __android_log_print(ANDROID_LOG_ERROR,
  28. TAG,
  29. "Unabletofindexceptionclass%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. "Failedthrowing'%s''%s'",
  37. className,msg);
  38. }
  39. return0;
  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. "FailedtoobtainJNIEnv");
  47. returnNULL;
  48. }
  49. returnenv;
  50. }
  51. /*
  52. *RegisternativeJNI-callablemethods.
  53. *
  54. *"className"lookslike"java/lang/String".
  55. */
  56. intjniRegisterNativeMethods(JNIEnv*env,
  57. constchar*className,
  58. constJNINativeMethod*gMethods,
  59. intnumMethods)
  60. /*从com_media_ffmpeg_FFMpegPlayer.cpp文件跳到此,完成最后的注册
  61. *向Dalvik虚拟机(即AndroidRuntime)登记传过来的参数gMethods[]所含的本地函数
  62. */
  63. {
  64. jclassclazz;
  65. __android_log_print(ANDROID_LOG_INFO,TAG,"Registering%snatives\n",className);
  66. clazz=env->FindClass(className);
  67. if(clazz==NULL){
  68. __android_log_print(ANDROID_LOG_ERROR,TAG,"Nativeregistrationunabletofindclass'%s'\n",className);
  69. return-1;
  70. }
  71. if(env->RegisterNatives(clazz,gMethods,numMethods)<0){
  72. __android_log_print(ANDROID_LOG_ERROR,TAG,"RegisterNativesfailedfor'%s'\n",className);
  73. return-1;
  74. }
  75. return0;
  76. }
  77. //Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数
  78. jintJNI_OnLoad(JavaVM*vm,void*reserved){
  79. JNIEnv*env=NULL;//定义JNIEnv
  80. jintresult=JNI_ERR;
  81. sVm=vm;
  82. /*JavaVM::GetEnv原型为jint(*GetEnv)(JavaVM*,void**,jint);
  83. *GetEnv()函数返回的Jni环境对每个线程来说是不同的,
  84. *由于Dalvik虚拟机通常是Multi-threading的。每一个线程调用JNI_OnLoad()时,
  85. *所用的JNIEnv是不同的,因此我们必须在每次进入函数时都要通过vm->GetEnv重新获取
  86. *
  87. */
  88. //得到JNIEnv
  89. if(vm->GetEnv((void**)&env,JNI_VERSION_1_4)!=JNI_OK){
  90. __android_log_print(ANDROID_LOG_ERROR,TAG,"GetEnvfailed!");
  91. returnresult;
  92. }
  93. __android_log_print(ANDROID_LOG_INFO,TAG,"loading...");
  94. /*开始注册
  95. *传入参数是JNIenv
  96. *由于下面有很多class,只以register_android_media_FFMpegPlayerAndroid(env)为例子
  97. */
  98. #ifdefBUILD_WITH_CONVERTOR
  99. if(register_android_media_FFMpeg(env)!=JNI_OK){
  100. __android_log_print(ANDROID_LOG_ERROR,TAG,"can'tloadandroid_media_FFMpeg");
  101. gotoend;
  102. }
  103. #endif
  104. if(register_android_media_FFMpegAVFormatContext(env)!=JNI_OK){
  105. __android_log_print(ANDROID_LOG_ERROR,TAG,"can'tloadandroid_media_FFMpegAVFormatContext");
  106. gotoend;
  107. }
  108. if(register_android_media_FFMpegAVCodecContext(env)!=JNI_OK){
  109. __android_log_print(ANDROID_LOG_ERROR,TAG,"can'tloadandroid_media_FFMpegAVCodecContext");
  110. gotoend;
  111. }
  112. if(register_android_media_FFMpegAVRational(env)!=JNI_OK){
  113. __android_log_print(ANDROID_LOG_ERROR,TAG,"can'tloadandroid_media_FFMpegAVRational");
  114. gotoend;
  115. }
  116. if(register_android_media_FFMpegAVInputFormat(env)!=JNI_OK){
  117. __android_log_print(ANDROID_LOG_ERROR,TAG,"can'tloadandroid_media_FFMpegAVInputFormat");
  118. gotoend;
  119. }
  120. if(register_android_media_FFMpegUtils(env)!=JNI_OK){
  121. __android_log_print(ANDROID_LOG_ERROR,TAG,"can'tloadandroid_media_FFMpegUtils");
  122. gotoend;
  123. }
  124. if(register_android_media_FFMpegAVFrame(env)!=JNI_OK){
  125. __android_log_print(ANDROID_LOG_ERROR,TAG,"can'tloadandroid_media_FFMpegAVFrame");
  126. gotoend;
  127. }
  128. #ifdefBUILD_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'tloadandroid_media_FFMpegPlayerAndroid");
  131. gotoend;
  132. }
  133. #endif
  134. __android_log_print(ANDROID_LOG_INFO,TAG,"loaded");
  135. result=JNI_VERSION_1_4;
  136. end:
  137. returnresult;
  138. }



[cpp] view plain copy
  1. //com_media_ffmpeg_FFMpegPlayer.cpp文件
  2. /*
  3. *
  4. *由于代码量较大,com_media_ffmpeg_FFMpegPlayer.cpp开始的一部分省略,只是贴出注册函数的相关部分。
  5. */
  6. staticconstchar*constkClassPathName="com/media/ffmpeg/FFMpegPlayer";
  7. /*
  8. *由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,
  9. *可多次调用registerNativeMethods()函数来更换本地函数的指针,
  10. *从而达到弹性调用本地函数的目的。
  11. */
  12. staticJNINativeMethodgMethods[]={
  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. intregister_android_media_FFMpegPlayerAndroid(JNIEnv*env){
  34. returnjniRegisterNativeMethods(env,kClassPathName,gMethods,sizeof(gMethods)/sizeof(gMethods[0]));
  35. /*跳到OnLoad.cpp文件中的
  36. *jintjniRegisterNativeMethods(JNIEnv*env,
  37. constchar*className,
  38. constJNINativeMethod*gMethods,
  39. intnumMethods)
  40. */
  41. }
转自: http://blog.csdn.net/luhuajcdd/article/details/7750146

更多相关文章

  1. C语言函数以及函数的使用
  2. Android中使用SAX方式解析XML文件
  3. 向eclipse中导入android中的sample样例+gen目录缺少R.java文件
  4. Ubuntu 虚拟机android平台搭建总结

随机推荐

  1. Android(安卓)SDK 5.0 这个语句带来折腾
  2. android preferenceActivity用法
  3. android绘图
  4. Android中的windowSoftInputMode属性详解
  5. Android学习笔记之Android包、ADB介绍
  6. 在Ubuntu中和Android中添加开机自启动的
  7. Android完美解决输入框EditText隐藏密码
  8. 直播代码Android怎么实现定时任务及闹钟?
  9. Android(安卓)自定义Html标签
  10. 讨论会1:Android消息循环机制原理与实现