JNI浅析

1 篇文章 0 订阅

 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 plain copy
 
  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 plain copy
 
  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 plain copy
 
  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 plain copy
 
  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类型的指针gMethodsgMethods指向一个JNINativeMethod数组,我们先看看JNINativeMethod这个结构体。

 

[java]  view plain copy
 
  1. typedef struct {  
  2.  const char* name; /*Java 中函数的名字*/  
  3.  const char* signature; /*描述了函数的参数和返回值*/  
  4.  void* fnPtr; /*函数指针,指向 C 函数*/  
  5.  } JNINativeMethod;  

再来看看gMethods数组

 

 

[java]  view plain copy
 
  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++相应的功能函数

 

原文出处:http://blog.csdn.net/mci2004/article/details/7211678

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值