Android的NDK開發(1)————Android JNI簡介與調用流程__android

來源:互聯網
上載者:User

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

 result = JNI_VERSION_1_4;

當沒有JNI_OnLoad()函數時,Android調試資訊會做出如下提示(No JNI_OnLoad found)

04-29 13:53:12.184: D/dalvikvm(361): Trying to load lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea9804-29 13:53:12.204: D/dalvikvm(361): Added shared lib /data/data/com.conowen.helloworld/lib/libHelloWorld.so 0x44edea9804-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為例詳細說一下:

//onLoad.cpp檔案#define TAG "ffmpeg_onLoad"#include <stdlib.h>#include <android/log.h>#include "jniUtils.h"extern "C" {extern int register_android_media_FFMpegAVRational(JNIEnv *env);#ifdef BUILD_WITH_CONVERTORextern int register_android_media_FFMpeg(JNIEnv *env);#endifextern int register_android_media_FFMpegAVFormatContext(JNIEnv *env);extern int register_android_media_FFMpegAVInputFormat(JNIEnv *env);}extern int register_android_media_FFMpegAVCodecContext(JNIEnv *env);extern int register_android_media_FFMpegUtils(JNIEnv *env);extern int register_android_media_FFMpegAVFrame(JNIEnv *env);#ifdef BUILD_WITH_PLAYERextern int register_android_media_FFMpegPlayerAndroid(JNIEnv *env);#endifstatic JavaVM *sVm;/* * Throw an exception with the specified class and an optional message. */int jniThrowException(JNIEnv* env, const char* className, const char* msg) {    jclass exceptionClass = env->FindClass(className);    if (exceptionClass == NULL) {        __android_log_print(ANDROID_LOG_ERROR,    TAG,    "Unable to find exception class %s",                    className);        return -1;    }    if (env->ThrowNew(exceptionClass, msg) != JNI_OK) {        __android_log_print(ANDROID_LOG_ERROR,    TAG,    "Failed throwing '%s' '%s'",    className, msg);    }    return 0;}JNIEnv* getJNIEnv() {    JNIEnv* env = NULL;    if (sVm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {    __android_log_print(ANDROID_LOG_ERROR,TAG,"Failed to obtain JNIEnv");    return NULL;    }    return env;}/* * Register native JNI-callable methods. * * "className" looks like "java/lang/String". */int jniRegisterNativeMethods(JNIEnv* env,                             const char* className,                             const JNINativeMethod* gMethods,                             int numMethods)/*從com_media_ffmpeg_FFMpegPlayer.cpp檔案跳到此,完成最後的註冊 * 向 Dalvik虛擬機器(即AndroidRuntime)登記傳過來的參數gMethods[]所含的本地函數 */{    jclass clazz;    __android_log_print(ANDROID_LOG_INFO, TAG, "Registering %s natives\n", className);    clazz = env->FindClass(className);    if (clazz == NULL) {        __android_log_print(ANDROID_LOG_ERROR, TAG, "Native registration unable to find class '%s'\n", className);        return -1;    }    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {        __android_log_print(ANDROID_LOG_ERROR, TAG, "RegisterNatives failed for '%s'\n", className);        return -1;    }    return 0;}//Dalvik虛擬機器載入C庫時,第一件事是調用JNI_OnLoad()函數jint JNI_OnLoad(JavaVM* vm, void* reserved) {    JNIEnv* env = NULL;//定義JNI Env    jint result = JNI_ERR;sVm = vm;/*JavaVM::GetEnv 原型為 jint (*GetEnv)(JavaVM*, void**, jint); * GetEnv()函數返回的  Jni 環境對每個線程來說是不同的, *  由於Dalvik虛擬機器通常是Multi-threading的。每一個線程調用JNI_OnLoad()時, *  所用的JNI Env是不同的,因此我們必須在每次進入函數時都要通過vm->GetEnv重新擷取 *   *///得到JNI Env    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {        __android_log_print(ANDROID_LOG_ERROR, TAG, "GetEnv failed!");        return result;    }    __android_log_print(ANDROID_LOG_INFO, TAG, "loading . . .");/*開始註冊 * 傳入參數是JNI env * 由於下面有很多class,只以register_android_media_FFMpegPlayerAndroid(env)為例子 */    #ifdef BUILD_WITH_CONVERTOR    if(register_android_media_FFMpeg(env) != JNI_OK) {        __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpeg");        goto end;    }#endif    if(register_android_media_FFMpegAVFormatContext(env) != JNI_OK) {    __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVFormatContext");        goto end;}    if(register_android_media_FFMpegAVCodecContext(env) != JNI_OK) {    __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVCodecContext");        goto end;}    if(register_android_media_FFMpegAVRational(env) != JNI_OK) {    __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVRational");    goto end;    }if(register_android_media_FFMpegAVInputFormat(env) != JNI_OK) {    __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVInputFormat");    goto end;    }if(register_android_media_FFMpegUtils(env) != JNI_OK) {__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegUtils");goto end;}if(register_android_media_FFMpegAVFrame(env) != JNI_OK) {__android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegAVFrame");goto end;}#ifdef BUILD_WITH_PLAYER    if(register_android_media_FFMpegPlayerAndroid(env) != JNI_OK) {//跳到----》com_media_ffmpeg_FFMpegPlayer.cpp檔案    __android_log_print(ANDROID_LOG_ERROR, TAG, "can't load android_media_FFMpegPlayerAndroid");    goto end;    }#endif    __android_log_print(ANDROID_LOG_INFO, TAG, "loaded");    result = JNI_VERSION_1_4;end:    return result;}



//com_media_ffmpeg_FFMpegPlayer.cpp檔案/* *  * 由於代碼量較大,com_media_ffmpeg_FFMpegPlayer.cpp開始的一部分省略,只是貼出註冊函數的相關部分。 */static const char* const kClassPathName = "com/media/ffmpeg/FFMpegPlayer";/* * 由於gMethods[]是一個<名稱,函數指標>對照表,在程式執行時, * 可多次調用registerNativeMethods()函數來更換本地函數的指標, * 從而達到彈性調用本地函數的目的。  */static JNINativeMethod gMethods[] = {    {"setDataSource",       "(Ljava/lang/String;)V",            (void *)com_media_ffmpeg_FFMpegPlayer_setDataSource},    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)com_media_ffmpeg_FFMpegPlayer_setVideoSurface},    {"prepare",             "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_prepare},    {"_start",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_start},    {"_stop",               "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_stop},    {"getVideoWidth",       "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getVideoWidth},    {"getVideoHeight",      "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getVideoHeight},    {"seekTo",              "(I)V",                             (void *)com_media_ffmpeg_FFMpegPlayer_seekTo},    {"_pause",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_pause},    {"isPlaying",           "()Z",                              (void *)com_media_ffmpeg_FFMpegPlayer_isPlaying},    {"getCurrentPosition",  "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getCurrentPosition},    {"getDuration",         "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getDuration},    {"_release",            "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_release},    {"_reset",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_reset},    {"setAudioStreamType",  "(I)V",                             (void *)com_media_ffmpeg_FFMpegPlayer_setAudioStreamType},    {"native_init",         "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_native_init},    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)com_media_ffmpeg_FFMpegPlayer_native_setup},    {"native_finalize",     "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_native_finalize},    {"native_suspend_resume", "(Z)I",                           (void *)com_media_ffmpeg_FFMpegPlayer_native_suspend_resume},};int register_android_media_FFMpegPlayerAndroid(JNIEnv *env) {return jniRegisterNativeMethods(env, kClassPathName, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));/*跳到OnLoad.cpp檔案中的 * jint jniRegisterNativeMethods(JNIEnv* env,                             const char* className,                             const JNINativeMethod* gMethods,                             int numMethods)                              */}



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.