专栏名称: 安卓开发精选
伯乐在线旗下账号,分享安卓应用相关内容,包括:安卓应用开发、设计和动态等。
目录
相关文章推荐
复利大王  ·  卷疯了:食堂档口末位淘汰 ·  11 小时前  
复利大王  ·  北大软微男在女厕所偷拍! ·  昨天  
复利大王  ·  美女硕士被妈妈的驭男术坑了 ·  2 天前  
复利大王  ·  青岛大所女律师cg事件 ·  2 天前  
51好读  ›  专栏  ›  安卓开发精选

Android JNI原理分析

安卓开发精选  · 公众号  · android  · 2016-10-08 08:48

正文

请到「今天看啥」查看全文


}

}

return 0 ;

}


gRegJNI数组,有100多个成员变量,定义在AndroidRuntime.cpp:


static const RegJNIRec gRegJNI [] = {

REG_JNI ( register_android_os_MessageQueue ),

REG_JNI ( register_android_os_Binder ),

...

};


该数组的每个成员都代表一个类文件的jni映射,其中REG_JNI是一个宏定义,在Zygote中介绍过,该宏的作用就是调用相应的方法。


2.2 如何查找native方法


当大家在看framework层代码时,经常会看到native方法,这是往往需要查看所对应的C++方法在哪个文件,对应哪个方法?下面从一个实例出发带大家如何查看java层方法所对应的native方法位置。


2.2.1 实例(一)


当分析Android消息机制源码,遇到MessageQueue.java中有多个native方法,比如:


private native void nativePollOnce ( long ptr , int timeoutMillis );


步骤1 : MessageQueue.java的全限定名为android.os.MessageQueue.java,方法名:android.os.MessageQueue.nativePollOnce(),而相对应的native层方法名只是将点号替换为下划线,可得android_os_MessageQueue_nativePollOnce()。 Tips : nativePollOnce ==> android_os_MessageQueue_nativePollOnce()


步骤2 : 有了native方法,那么接下来需要知道该native方法所在那个文件。前面已经介绍过Android系统启动时就已经注册了大量的JNI方法,见AndroidRuntime.cpp的gRegJNI数组。这些注册方法命令方式:


register_ [ 包名 ] _ [ 类名 ]


那么MessageQueue.java所定义的jni注册方法名应该是register_android_os_MessageQueue,的确存在于gRegJNI数组,说明这次JNI注册过程是有开机过程完成的。 该方法在AndroidRuntime.cpp申明为extern方法:


extern int register_android_os_MessageQueue ( JNIEnv * env );


这些extern方法绝大多数位于/framework/base/core/jni/目录,大多数情况下native文件命名方式:


[ 包名 ] _ [ 类名 ]. cpp

[ 包名 ] _ [ 类名 ]. h


Tips : MessageQueue.java ==> android_os_MessageQueue.cpp


打开android_os_MessageQueue.cpp文件,搜索android_os_MessageQueue_nativePollOnce方法,这便找到了目标方法:


static void android_os_MessageQueue_nativePollOnce ( JNIEnv * env , jobject obj ,

jlong ptr , jint timeoutMillis ) {

NativeMessageQueue * nativeMessageQueue = reinterpret_cast NativeMessageQueue *> ( ptr );

nativeMessageQueue -> pollOnce ( env , obj , timeoutMillis );

}


到这里完成了一次从Java层方法搜索到所对应的C++方法的过程。


2.2.2 实例(二)


对于native文件命名方式,有时并非[包名]_[类名].cpp,比如Binder.java


Binder.java所对应的native文件:android_util_Binder.cpp


public static final native int getCallingPid ();


根据实例(一)方式,找到getCallingPid ==> android_os_Binder_getCallingPid(),并且在AndroidRuntime.cpp中的gRegJNI数组中找到register_android_os_Binder。


按实例(一)方式则native文名应该为android_os_Binder.cpp,可是在/framework/base/core/jni/目录下找不到该文件,这是例外的情况。其实真正的文件名为android_util_Binder.cpp,这就是例外,这一点有些费劲,不明白为何google要如此打破规律的命名。


static jint android_os_Binder_getCallingPid ( JNIEnv * env , jobject clazz )

{

return IPCThreadState :: self () -> getCallingPid ();

}


有人可能好奇,既然如何遇到打破常规的文件命令,怎么办?这个并不难,首先,可以尝试在/framework/base/core/jni/中搜索,对于binder.java,可以直接搜索binder关键字,其他也类似。如果这里也找不到,可以通过grep全局搜索android_os_Binder_getCallingPid这个方法在哪个文件。


jni存在的常见目录:


  • /framework/base/core/jni/

  • /framework/base/services/core/jni/

  • /framework/base/media/jni/


2.2.3 实例(三)


前面两种都是在Android系统启动之初,便已经注册过JNI所对应的方法。 那么如果程序自己定义的jni方法,该如何查看jni方法所在位置呢?下面以MediaPlayer.java为例,其包名为android.media:


public class MediaPlayer {

static {

System . loadLibrary ( "media_jni" );

native_init ();

}

private static native final void native_init ();

...

}


通过static静态代码块中System.loadLibrary方法来加载动态库,库名为media_jni, Android平台则会自动扩展成所对应的libmedia_jni.so库。 接着通过关键字native加在native_init方法之前,便可以在java层直接使用native层方法。


接下来便要查看libmedia_jni.so库定义所在文件,一般都是通过Android.mk文件定义LOCAL_MODULE:= libmedia_jni,可以采用grep或者mgrep来搜索包含libmedia_jni字段的Android.mk所在路径。


搜索可知,libmedia_jni.so位于/frameworks/base/media/jni/Android.mk。用前面实例(一)中的知识来查看相应的文件和方法名分别为:


android_media_MediaPlayer . cpp

android_media_MediaPlayer_native_init ()


再然后,你会发现果然在该Android.mk所在目录/frameworks/base/media/jni/中找到android_media_MediaPlayer.cpp文件,并在文件中存在相应的方法:


static void

android_media_MediaPlayer_native_init ( JNIEnv * env )

{

jclass clazz ;

clazz = env -> FindClass ( "android/media/MediaPlayer" );

fields . context = env -> GetFieldID ( clazz , "mNativeContext" , "J" );

...

}


Tips :MediaPlayer.java中的native_init方法所对应的native方法位于/frameworks/base/media/jni/目录下的android_media_MediaPlayer.cpp文件中的android_media_MediaPlayer_native_init方法。


2.3 小结


JNI作为连接Java世界和C/C++世界的桥梁,很有必要掌握。看完本文,至少能掌握在分析Android源码过程中如何查找native方法。首先要明白native方法名和文件名的命名规律,其次要懂得该如何去搜索代码。 JNI方式注册无非是Android系统启动过程中Zygote注册以及通过System.loadLibrary方式注册,对于系统启动过程注册的,可以通过查询AndroidRuntime.cpp中的gRegJNI是否存在对应的register方法,如果不存在,则大多数情况下是通过LoadLibrary方式来注册。


三、 JNI原理分析


再进一步来分析,Java层与native层方法是如何注册并映射的,继续以MediaPlayer为例。


在文件MediaPlayer.java中调用System.loadLibrary("media_jni")把libmedia_jni.so动态库加载到内存。接下来,以loadLibrary为起点展开JNI注册流程的过程分析。


3.1 loadLibrary


[System.java]


public static void loadLibrary ( String libName ) {

//接下来调用Runtime方法

Runtime . getRuntime (). loadLibrary ( libName , VMStack . getCallingClassLoader ());

}


[Runtime.java]


void loadLibrary ( String libraryName , ClassLoader loader ) {

//loader不会空,则进入该分支

if ( loader !=







请到「今天看啥」查看全文