Android jni的调用过程

原创 2015年11月19日 07:11:48

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 plaincopy在CODE上查看代码片派生到我的代码片
  1. result = JNI_VERSION_1_4;  

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

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  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.   
  3. #define TAG "ffmpeg_onLoad"  
  4.   
  5. #include <stdlib.h>  
  6. #include <android/log.h>  
  7. #include "jniUtils.h"  
  8.   
  9. extern "C" {  
  10.   
  11. extern int register_android_media_FFMpegAVRational(JNIEnv *env);  
  12.   
  13. #ifdef BUILD_WITH_CONVERTOR  
  14. extern int register_android_media_FFMpeg(JNIEnv *env);  
  15. #endif  
  16.   
  17. extern int register_android_media_FFMpegAVFormatContext(JNIEnv *env);  
  18. extern int register_android_media_FFMpegAVInputFormat(JNIEnv *env);  
  19.   
  20. }  
  21.   
  22. extern int register_android_media_FFMpegAVCodecContext(JNIEnv *env);  
  23. extern int register_android_media_FFMpegUtils(JNIEnv *env);  
  24. extern int register_android_media_FFMpegAVFrame(JNIEnv *env);  
  25.   
  26. #ifdef BUILD_WITH_PLAYER  
  27. extern int register_android_media_FFMpegPlayerAndroid(JNIEnv *env);  
  28. #endif  
  29.   
  30. static JavaVM *sVm;  
  31.   
  32. /* 
  33.  * Throw an exception with the specified class and an optional message. 
  34.  */  
  35. int jniThrowException(JNIEnv* env, const char* className, const char* msg) {  
  36.     jclass exceptionClass = env->FindClass(className);  
  37.     if (exceptionClass == NULL) {  
  38.         __android_log_print(ANDROID_LOG_ERROR,  
  39.                 TAG,  
  40.                 "Unable to find exception class %s",  
  41.                         className);  
  42.         return -1;  
  43.     }  
  44.   
  45.     if (env->ThrowNew(exceptionClass, msg) != JNI_OK) {  
  46.         __android_log_print(ANDROID_LOG_ERROR,  
  47.                 TAG,  
  48.                 "Failed throwing '%s' '%s'",  
  49.                 className, msg);  
  50.     }  
  51.     return 0;  
  52. }  
  53.   
  54. JNIEnv* getJNIEnv() {  
  55.     JNIEnv* env = NULL;  
  56.     if (sVm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
  57.         __android_log_print(ANDROID_LOG_ERROR,  
  58.                             TAG,  
  59.                             "Failed to obtain JNIEnv");  
  60.         return NULL;  
  61.     }  
  62.     return env;  
  63. }  
  64.   
  65. /* 
  66.  * Register native JNI-callable methods. 
  67.  * 
  68.  * "className" looks like "java/lang/String". 
  69.  */  
  70. int jniRegisterNativeMethods(JNIEnv* env,  
  71.                              const char* className,  
  72.                              const JNINativeMethod* gMethods,  
  73.                              int numMethods)  
  74. /*从com_media_ffmpeg_FFMpegPlayer.cpp文件跳到此,完成最后的注册 
  75.  * 向 Dalvik虚拟机(即AndroidRuntime)登记传过来的参数gMethods[]所含的本地函数 
  76.  */  
  77. {  
  78.     jclass clazz;  
  79.   
  80.     __android_log_print(ANDROID_LOG_INFO, TAG, "Registering %s natives\n", className);  
  81.     clazz = env->FindClass(className);  
  82.     if (clazz == NULL) {  
  83.         __android_log_print(ANDROID_LOG_ERROR, TAG, "Native registration unable to find class '%s'\n", className);  
  84.         return -1;  
  85.     }  
  86.     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {  
  87.         __android_log_print(ANDROID_LOG_ERROR, TAG, "RegisterNatives failed for '%s'\n", className);  
  88.         return -1;  
  89.     }  
  90.     return 0;  
  91. }  
  92. //Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数  
  93. jint JNI_OnLoad(JavaVM* vm, void* reserved) {  
  94.     JNIEnv* env = NULL;//定义JNI Env  
  95.     jint result = JNI_ERR;  
  96.     sVm = vm;  
  97.     /*JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint); 
  98.      * GetEnv()函数返回的  Jni 环境对每个线程来说是不同的,     
  99.      *  由于Dalvik虚拟机通常是Multi-threading的。每一个线程调用JNI_OnLoad()时, 
  100.      *  所用的JNI Env是不同的,因此我们必须在每次进入函数时都要通过vm->GetEnv重新获取 
  101.      *   
  102.      */  
  103.     //得到JNI Env  
  104.     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
  105.         __android_log_print(ANDROID_LOG_ERROR, TAG, "GetEnv failed!");  
  106.         return result;  
  107.     }  
  108.   
  109.     __android_log_print(ANDROID_LOG_INFO, TAG, "loading . . .");  
  110.   
  111. /*开始注册 
  112.  * 传入参数是JNI env 
  113.  * 由于下面有很多class,只以register_android_media_FFMpegPlayerAndroid(env)为例子 
  114.  */  
  115.       
  116. #ifdef BUILD_WITH_CONVERTOR  
  117.     if(register_android_media_FFMpeg(env) != JNI_OK) {  
  118.         __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpeg");  
  119.         goto end;  
  120.     }  
  121. #endif  
  122.   
  123.     if(register_android_media_FFMpegAVFormatContext(env) != JNI_OK) {  
  124.         __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVFormatContext");  
  125.         goto end;  
  126.     }  
  127.   
  128.     if(register_android_media_FFMpegAVCodecContext(env) != JNI_OK) {  
  129.         __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVCodecContext");  
  130.         goto end;  
  131.     }  
  132.   
  133.     if(register_android_media_FFMpegAVRational(env) != JNI_OK) {  
  134.         __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVRational");  
  135.         goto end;  
  136.     }  
  137.       
  138.     if(register_android_media_FFMpegAVInputFormat(env) != JNI_OK) {  
  139.         __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVInputFormat");  
  140.         goto end;  
  141.     }  
  142.       
  143.     if(register_android_media_FFMpegUtils(env) != JNI_OK) {  
  144.         __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegUtils");  
  145.         goto end;  
  146.     }  
  147.   
  148.     if(register_android_media_FFMpegAVFrame(env) != JNI_OK) {  
  149.         __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVFrame");  
  150.         goto end;  
  151.     }  
  152.   
  153. #ifdef BUILD_WITH_PLAYER  
  154.     if(register_android_media_FFMpegPlayerAndroid(env) != JNI_OK) {//跳到----》com_media_ffmpeg_FFMpegPlayer.cpp文件  
  155.         __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegPlayerAndroid");  
  156.         goto end;  
  157.     }  
  158. #endif  
  159.   
  160.     __android_log_print(ANDROID_LOG_INFO, TAG, "loaded");  
  161.   
  162.     result = JNI_VERSION_1_4;  
  163.   
  164. end:  
  165.     return result;  
  166. }  



  1. //com_media_ffmpeg_FFMpegPlayer.cpp文件  
  2. /* 
  3.  *  
  4.  * 由于代码量较大,com_media_ffmpeg_FFMpegPlayer.cpp开始的一部分省略,只是贴出注册函数的相关部分。 
  5.  */  
  6. static const charconst 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.   
  34. int register_android_media_FFMpegPlayerAndroid(JNIEnv *env) {  
  35.     return jniRegisterNativeMethods(env, kClassPathName, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));  
  36.     /*跳到OnLoad.cpp文件中的 
  37.      * jint jniRegisterNativeMethods(JNIEnv* env, 
  38.                              const char* className, 
  39.                              const JNINativeMethod* gMethods, 
  40.                              int numMethods) 
  41.                               
  42.      */  
  43. }  
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android动态调试so库JNI_Onload函数-----基于IDA实现

之前看过吾爱破解论坛一个关于Android'逆向动态调试的经验总结帖,那个帖子写的很好,对Android的脱壳和破解很有帮助,之前我们老师在上课的时候也讲过集中调试的方法,但是现在不太实用。对吾爱破解...

JNI注册调用完整过程-安卓4.4

在Android系统中,JNI方法是以C/C++语言来实现的,然后编译在一个so文件里面,以我之前的例子为例Android Studio使用JNI,调用之前要加载到当前应用程序的进程的地址空间中: ...

Android使用post方式上传图片到服务器的方法

本文实例讲述了Android使用post方式上传图片到服务器的方法。分享给大家供大家参考,具体如下: /** * 上传文件到服务器类 * * @author tom */ public...

android post上传文件到服务器

android 通过post请求上传文件到服务器

Android开发:安装NDK,移植OpenCV2.3.1,JNI调用OpenCV全过程

开发环境:Fedora14 , 操作中以root权限操作,这年头Android移植上OpenCV就强大了,可以做很多复杂的视频分析、图像处理工作了!如火灾、人脸、视频行为的分析等。欢迎Android爱...

Android调用jni全过程,方便以后操作。

文章来源 :http://www.cnblogs.com/stay/archive/2011/08/28/2155385.html 个人记录下调用jni全过程,省的自己忘记了。哎。 有些需要准备的...
  • Ah_Kin
  • Ah_Kin
  • 2013年10月17日 14:39
  • 548

eclipse as android jni 和 ndk以及调用过程--tread

乱叨叨 Eclipse 配置 1下载所需要的ndk版本 2新建一个Android工程 3编写java类对CC 进行调用 4生成h头文件 5根据头文件编写自己的cc文件并编译 6编译多个源文件 7面向多...

Android调用jni全过程示例

个人记录下调用jni全过程,省的自己忘记了。哎。 有些需要准备的地方,ndk环境,ndk源码一份,cygwin要装好,配好profile。java要配好环境变量。 1.首先新建一个工程,建个类...

Android调用jni全过程

Android调用jni全过程 个人记录下调用jni全过程,省的自己忘记了。哎。 有些需要准备的地方,ndk环境,ndk源码一份,cygwin要装好,配好profile。java要配好环境...

Android apk安装过程及Java、JNI读取安装包内assets资源文件的两种方法(附源码实例)

问题背景:在PC上的程序可以轻松使用./或不用指明,默认读取的就是程序所在路径内的文件。但在Android上,应用程序被打包成apk,程序运行时无法直接获取apk(压缩包)内的文件。但在一些特殊场合,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android jni的调用过程
举报原因:
原因补充:

(最多只允许输入30个字)