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



    http://blog.csdn.net/conowen/article/details/7521340


    /********************************************************************************************
     * author:conowen@大钟                                                                                                                          
     * E-mail:conowen@hotmail.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.   
    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 ", 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' ", 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' ", 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.      */  


  • 相关阅读:
    百度文库:网站镜像站点解决方案
    百度文库:WEB网站架构分析HTML静态化
    61条面向对象设计的经验原则
    oracle 定时任务例子【项目例子】
    javascript的事件机制(百度文库)
    【转】Oracle回收站(recyclebin)
    java十大低级错误和常见注意点
    JAVA UUID 生成
    Oracle in和exists效率问题分析
    http的长连接和短连接(数据库也一样)
  • 原文地址:https://www.cnblogs.com/ztguang/p/12645157.html
Copyright © 2020-2023  润新知