Android JNI淺析(1)

來源:互聯網
上載者:User

JNI是Java Native Interface的縮寫,JNI是一種機制,有了它就可以在java程式中調用其他native代碼,或者使native代碼調用java層的代碼。也就是說,有了JNI我們可以使Android項目中,java層與native層各自發揮所長並相互配合。如所示,JNI在Android中所處的位置。

  

                                                                          

                                                                好吧誰不知道JNI應該在JAVA和Native的中間呢?


        JNI相對與native層來說是一個介面,java層的程式想訪問native層,必須通過JNI,反過來也一樣。下面我們來看幾個問題。

1,如何告訴VM(虛擬機器)java層需要調用native層的哪些libs?

        我們知道java程式是運行在VM上的,而Native層的libs則不然。所以為了讓java層能訪問native層的libs,必須得告訴VM要使用哪些native層的libs。下面看一段代碼

[java]
view plaincopy
  1. public class MediaPlayer    
  2.  {    
  3.      ...    
  4.      
  5.      static {    
  6.          System.loadLibrary("media_jni");    
  7.          native_init();    
  8.      }    
  9.      
  10.       ...    
  11.      
  12.      private native final void native_setup(Object mediaplayer_this);    
  13.           
  14.       ...    
  15.   }  

可以看到上面的代碼中,在MediaPlayer類中有一段static塊包圍起來的代碼,其中System.loadLibrary("media_jni")就是告訴VM去載入libmedia_jni.so這個動態庫,那麼這個動態庫什麼時候被載入呢?因為static語句塊的原因,所以在MediaPlayer第一次執行個體化的時候就會被載入了。這段代碼中,我們還看到了一個函數native_init(),該函數被申明為native型,就是告訴VM該函數由native層來實現。

2,如何做到java層到native層的映射。

        事實上我想表達的意思是,如何完成java層的代碼到native層代碼的映射,例如上面的代碼中有一個native函數native_init(),那麼如何使這個函數映射到一個由C/C++(或者其他語言)實現的具體函數呢?PS:本菜鳥,表達能力欠缺,不知道大家有沒有看明白。

             當VM執行到System.loadLibrary()的時候就會去執行native libs中的JNI_OnLoad(JavaVM* vm, void* reserved)函數,因為JNI_OnLoad函數是從java層進入native層第一個調用的方法,所以可以在JNI_OnLoad函數中完成一些native層組件的初始化工作,同時更加重要的是,通常在JNI_jint
JNI_OnLoad(JavaVM* vm, void* reserved)函數中會註冊java層的native方法。下面看一段代碼:

[java]
view plaincopy
  1. jint JNI_OnLoad(JavaVM* vm, void* reserved)  
  2. {  
  3.     JNIEnv* env = NULL;  
  4.     jint result = -1;  
  5.     //判斷一下JNI的版本   
  6.     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
  7.         LOGE("ERROR: GetEnv failed\n");  
  8.         goto bail;  
  9.     }  
  10.     assert(env != NULL);  
  11.   
  12.     if (register_android_media_MediaPlayer(env) < 0) {  
  13.         LOGE("ERROR: MediaPlayer native registration failed\n");  
  14.         goto bail;  
  15.     }  
  16.   
  17.     if (register_android_media_MediaRecorder(env) < 0) {  
  18.         LOGE("ERROR: MediaRecorder native registration failed\n");  
  19.         goto bail;  
  20.     }  
  21.   
  22.     if (register<span style="font-size:16px;">_android_media_MediaScanner(env) < 0) {  
  23.         LOGE("ERROR: MediaScanner native registration failed\n");  
  24.         goto bail;  
  25.     }</span>  
  26.   
  27.     if (register_android_media_MediaMetadataRetriever(env) < 0) {  
  28.         LOGE("ERROR: MediaMetadataRetriever native registration failed\n");  
  29.         goto bail;  
  30.     }  
  31.   
  32.     if (register_android_media_AmrInputStream(env) < 0) {  
  33.         LOGE("ERROR: AmrInputStream native registration failed\n");  
  34.         goto bail;  
  35.     }  
  36.   
  37.     if (register_android_media_ResampleInputStream(env) < 0) {  
  38.         LOGE("ERROR: ResampleInputStream native registration failed\n");  
  39.         goto bail;  
  40.     }  
  41.   
  42.     if (register_android_media_MediaProfiles(env) < 0) {  
  43.         LOGE("ERROR: MediaProfiles native registration failed");  
  44.         goto bail;  
  45.     }  
  46.   
  47.     /* success -- return valid version number */  
  48.     result = JNI_VERSION_1_4;  
  49.   
  50. bail:  
  51.     return result;  
  52. }  

上面這段代碼的JNI_OnLoad(JavaVM* vm, void* reserved)函數實現與libmedia_jni.so庫中。上面的代碼中調用了一些形如register_android_media_MediaPlayer(env)的函數,這些函數的作用是註冊native method。我們來看看函數register_android_media_MediaPlayer(env)的實現。

[java]
view plaincopy
  1. // This function only registers the native methods  
  2. static int register_android_media_MediaPlayer(JNIEnv *env)  
  3. {  
  4.     return AndroidRuntime::registerNativeMethods(env,  
  5.                 "android/media/MediaPlayer", gMethods, NELEM(gMethods));  
[java]
view plaincopy
  1. /* 
  2.  * Register native methods using JNI. 
  3.  */  
  4. /*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,  
  5.     const char* className, const JNINativeMethod* gMethods, int numMethods)  
  6. {  
  7.     return jniRegisterNativeMethods(env, className, gMethods, numMethods);  
  8. }  

最終jniRegisterNativeMethods函數完成java標準的native函數的映射工作。下面我們來具體的看看上面這個函數中各個參數的意義。

a,JNIEnv* env,關於JNIEnv我在google上找到了這些資訊:

JNI defines two key data structures, "JavaVM" and "JNIEnv". Both of these are essentiallypointers to pointers to function tables. (In the C++ version, they're classes with apointer to a function table and a member function for each JNI function that indirects
throughthe table.) The JavaVM provides the "invocation interface" functions,which allow you to create and destroy a JavaVM. In theory you can have multiple JavaVMs per process,but Android only allows one.

The JNIEnv provides most of the JNI functions. Your native functions all receive a JNIEnv asthe first argument.

The JNIEnv is used for thread-local storage. For this reason, you cannot share a JNIEnv between threads.If a piece of code has no other way to get its JNIEnv, you should sharethe JavaVM, and useGetEnv to discover the thread's
JNIEnv. (Assuming it has one; see AttachCurrentThread below.)

這裡需要注意一點的是,JNIEnv是一個線程的局部變數,這以為這JNIEnv是存在與多線程環境下的,因為 VM 通常是多執行緒(Multi-threading)的執行環境。每一個執行緒在呼叫JNI_OnLoad()時,所傳遞進來的 JNIEnv 指標值都是不同的。為了配合這種多執行緒的環境,C/C++組件開發人員在撰寫本地函數時,可藉由 JNIEnv 指標值之不同而避免執行緒的資料衝突問題,才能確保所寫的本地函數能安全地在 Android 的多執行緒 VM 裡安全地執行。基於這個理由,當在
呼叫 C/C++ 組件的函數時,都會將 JNIEnv 指標值傳遞給它。

b,char* className,這個沒什麼好說的,java空間中類名,其中包含了包名。

c,JNINativeMethod* gMethods,傳遞進去的是一個JNINativeMethod類型的指標gMethods,gMethods指向一個JNINativeMethod數組,我們先看看JNINativeMethod這個結構體。

[java]
view plaincopy
  1. typedef struct {  
  2.  const char* name; /*Java 中函數的名字*/  
  3.  const char* signature; /*描述了函數的參數和傳回值*/  
  4.  void* fnPtr; /*函數指標,指向 C 函數*/  
  5.  } JNINativeMethod;  

再來看看gMethods數組

[java]
view plaincopy
  1. static JNINativeMethod gMethods[] = {  
  2.     {"setDataSource",       "(Ljava/lang/String;)V",            (void *)android_media_MediaPlayer_setDataSource},  
  3.     。。。  
  4.     {"setAuxEffectSendLevel", "(F)V",                           (void *)android_media_MediaPlayer_setAuxEffectSendLevel},  
  5.     {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},  
  6.     {"getOrganDBIndex",     "(II)I",                            (void *)android_media_MediaPlayer_getOrganDBIndex},  
  7. };  

d,int numMethods,不解釋。


這樣一來就完成了java native函數到到JNI層函數的映射。當然具體功能實現還是由JNI層函數來調用C/C++相應的功能函數

-------------------------------------------至此只講解了JNI大致流程,下次會具體講解一下JNI的細節,本菜鳥能力有限,blog中若有錯誤的地方還請給為指正,謝謝。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.