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。下面看一段代码
- public class MediaPlayer
- {
- ...
- static {
- System.loadLibrary("media_jni");
- native_init();
- }
- ...
- private native final void native_setup(Object mediaplayer_this);
- ...
- }
可以看到上面的代码中,在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方法。下面看一段代码:
- jint JNI_OnLoad(JavaVM* vm, void* reserved)
- {
- JNIEnv* env = NULL;
- jint result = -1;
- //判断一下JNI的版本
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- LOGE("ERROR: GetEnv failed\n");
- goto bail;
- }
- assert(env != NULL);
- if (register_android_media_MediaPlayer(env) < 0) {
- LOGE("ERROR: MediaPlayer native registration failed\n");
- goto bail;
- }
- if (register_android_media_MediaRecorder(env) < 0) {
- LOGE("ERROR: MediaRecorder native registration failed\n");
- goto bail;
- }
- if (register<span style="font-size:16px;">_android_media_MediaScanner(env) < 0) {
- LOGE("ERROR: MediaScanner native registration failed\n");
- goto bail;
- }</span>
- if (register_android_media_MediaMetadataRetriever(env) < 0) {
- LOGE("ERROR: MediaMetadataRetriever native registration failed\n");
- goto bail;
- }
- if (register_android_media_AmrInputStream(env) < 0) {
- LOGE("ERROR: AmrInputStream native registration failed\n");
- goto bail;
- }
- if (register_android_media_ResampleInputStream(env) < 0) {
- LOGE("ERROR: ResampleInputStream native registration failed\n");
- goto bail;
- }
- if (register_android_media_MediaProfiles(env) < 0) {
- LOGE("ERROR: MediaProfiles native registration failed");
- goto bail;
- }
- /* success -- return valid version number */
- result = JNI_VERSION_1_4;
- bail:
- return result;
- }
上面这段代码的JNI_OnLoad(JavaVM* vm, void* reserved)函数实现与libmedia_jni.so库中。上面的代码中调用了一些形如register_android_media_MediaPlayer(env)的函数,这些函数的作用是注册native method。我们来看看函数register_android_media_MediaPlayer(env)的实现。
- // This function only registers the native methods
- static int register_android_media_MediaPlayer(JNIEnv *env)
- {
- return AndroidRuntime::registerNativeMethods(env,
- "android/media/MediaPlayer", gMethods, NELEM(gMethods));
- /*
- * Register native methods using JNI.
- */
- /*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
- const char* className, const JNINativeMethod* gMethods, int numMethods)
- {
- return jniRegisterNativeMethods(env, className, gMethods, numMethods);
- }
最终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这个结构体。
- typedef struct {
- const char* name; /*Java 中函数的名字*/
- const char* signature; /*描述了函数的参数和返回值*/
- void* fnPtr; /*函数指针,指向 C 函数*/
- } JNINativeMethod;
再来看看gMethods数组
- static JNINativeMethod gMethods[] = {
- {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource},
- 。。。
- {"setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
- {"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
- {"getOrganDBIndex", "(II)I", (void *)android_media_MediaPlayer_getOrganDBIndex},
- };
d,int numMethods,不解释。
这样一来就完成了java native函数到到JNI层函数的映射。当然具体功能实现还是由JNI层函数来调用C/C++相应的功能函数
原文出处:http://blog.csdn.net/mci2004/article/details/7211678
相关推荐
《Android JNI 浅析》 Android JNI,全称为Java Native Interface,是Java平台的一个核心特性,它提供了一种方式让Java应用程序能够与本地C/C++代码进行交互。JNI使得开发者可以充分利用Java和C/C++的优点,例如...
- "Android_JNI浅析":可能从更直观的角度介绍了JNI,适合初学者快速理解JNI的工作原理。 - "jni详解":这可能是对JNI的深入解析,包括复杂的API使用和最佳实践。 了解和掌握JNI,开发者可以更好地利用Android...
Android 下通过 JNI 监控 WiFi 网络连接、dhcpcd 执行和 Power 电源控制 Android 作为一个开源的移动操作系统,具有广泛的应用前景,而 WiFi 网络连接、dhcpcd 执行和 Power 电源控制是 Android 中的三个重要组件。...
在深入探讨如何在Android系统中利用JNI(Java Native Interface)技术来监控WiFi网络连接状态、DHCP客户端守护进程(dhcpcd)的执行以及管理电源控制(power),我们首先需要理解基本概念与原理。 ### Android与JNI...
在Android应用开发中,NDK(原生开发工具包)允许开发者利用C或C++来编写程序的部分,这样的方式称为JNI(Java Native Interface)编程。JNI编程使得Java代码可以调用本地代码,增强了程序的性能,并可以复用已有的C/C++...
该方法通常用于在对象被销毁前执行一些清理操作,例如释放JNI(Java Native Interface)所分配的本地资源。然而,依赖`finalize()`来释放资源不是最佳实践,因为它的调用时机不确定且可能导致性能问题。建议避免过度...
Android.mk是Android Native Development Kit (NDK) 中的关键文件,用于构建C/C++原生代码,通常是用于实现Java平台上的本地方法(JNI)。通过编写Android.mk,开发者能够控制如何编译、链接和打包本地库,使其能与...
- 本地方法栈JNI中栈帧引用的对象:JNI(Java Native Interface)允许Java代码调用本地(非Java)代码,本地方法栈中的引用也需要考虑。 这一过程通常会导致Stop-The-World(STW)事件,即所有线程暂停,直到...
博客:NDK-JNI实战教程(三) 从比Hello World稍复杂点儿的NDK例子说说模板 博客文章链接---------实例代码工程 博客:facebook Fresco框架库源使用基础 博客文章链接---------实例代码工程 博客:浅谈MVP实现...
2. **可达性分析算法**:通过GC Roots(如虚拟机栈、方法区的静态属性和常量、本地方法栈中的JNI引用)作为起点,如果对象无法从GC Roots到达,则认为对象不可达,可以被回收。这种方法能有效处理循环引用问题。 **...
这是因为`start()`方法内部会调用`start0()`,这是一个本地方法(`native`),它利用了JNI(Java Native Interface)技术来与操作系统进行交互,创建并启动一个真实的系统线程。 使用`Runnable`接口的好处是它可以...
我的博客实例库( ##示例索引###博客:NDK-JNI实战教程(三)从比Hello World稍复杂点儿的NDK例子说说模板---------###博客:facebook Fresco框架库源使用基础---------###博客:浅谈MVP实现Android应用层开发------...
4.6 浅析dalvik虚拟机JIT技术的实现... ...133 4.7 应用程序的签名(Signature) ... ...135 4.8 应用的权限... ..138 4.9 屏幕密度Density ... ..140 3 4.10 Prelink实现的源码分析 ... ...142 4.11 适配...