概述
JNI,即Java Native Interface,Java本地调用。通过JNI可以实现:
Java程序函数可以调用Natvie语言(C/C++)写的函数
Natvie程序函数可以调用Java层的函数
使用Android Xref提供的 Android 9.0.0_r3的源码
完成两件事:
加载JNI库
Java的native函数
frameworks/base/media/java/android/media/MediaScanner.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class MediaScanner implements AutoCloseable { static { System.loadLibrary("media_jni" ); native_init(); } private final static String TAG = "MediaScanner" ; ... private native void processDirectory (String path, MediaScannerClient client) ; private native boolean processFile (String path, String mimeType, MediaScannerClient client) ; private native void setLocale (String locale) ; public native byte [] extractAlbumArt(FileDescriptor fd); private static native final void native_init () ; private native final void native_setup () ; private native final void native_finalize () ;
动态库是运行时加载的库。如果Java要调用native函数,必须通过一个位于JNI层的动态库实现。通常是在类的static语句中加载,调用System.loadLibrary
方法就可以加载。
函数名前有Java的关键字native
的函数表示将由JNI层实现。
因而Java层只需要两项工作:加载对应的JNI库,和声明由关键字native修饰的函数
JNI对应的文件是frameworks/base/media/jni/android_media_MediaScanner.cpp
frameworks/base/media/jni/android_media_MediaScanner.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static void android_media_MediaScanner_native_init(JNIEnv *env) { ALOGV("native_init" ); jclass clazz = env->FindClass(kClassMediaScanner); if (clazz == NULL ) { return ; } fields.context = env->GetFieldID(clazz, "mNativeContext" , "J" ); if (fields.context == NULL ) { return ; } } ...
java层的native_init函数对应android_media_MediaScanner_native_init
。通过文件路径来命名,观察两个文件的路径:
frameworks/base/media/java/android/media/MediaScanner.java
frameworks/base/media/jni/android_media_MediaScanner.cpp
注册JNI函数 java层的MediaScanner.java函数native_init位于android.media包中,全路径名是:android/media/MediaScanner.java
,对应JNI层函数的名字。JNI层将Java函数名称(包含包名)中的.
转换成_
,通过这种方式,native_init对应JNI的函数。
JNI函数注册的意思是将java层的native函数和JNI层对应的实现函数关联起来。注册有两种方式:静态方法和动态注册
静态注册
根据函数名来找对应的JNI函数。
(1) 编写Java代码,编译生成.class文件
(2) 使用Java的工具命令javah -o output packagename.classname
,生成一个output.h
的JNI头文件,里面声明了对应的JNI函数,只要实现里面的函数即可。
动态注册
因为Java native函数和JNI函数是一一对应的,所以存在一种JNINativeMethod
的结构记录这种一一对应的关系。
例如frameworks/base/media/jni/android_media_MediaScanner.cpp文件中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ... static const JNINativeMethod gMethods[] = { ...... { "native_setup" , "()V" , (void *)android_media_MediaScanner_native_setup }, { "native_finalize" , "()V" , (void *)android_media_MediaScanner_native_finalize }, }; int register_android_media_MediaScanner (JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, kClassMediaScanner, gMethods, NELEM(gMethods)); }
当Java层通过System.loadLibrary
加载完JNI动态库后,会查找该库中JNI_OnLoad
函数,然后调用他,之后完成动态注册。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 jint JNI_OnLoad (JavaVM* vm, void * ) { JNIEnv* env = NULL ; jint result = -1 ; if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) { ALOGE("ERROR: GetEnv failed\n" ); goto bail; } assert(env != NULL ); ...... if (register_android_media_MediaRecorder(env) < 0 ) { ALOGE("ERROR: MediaRecorder native registration failed\n" ); goto bail; } if (register_android_media_MediaScanner(env) < 0 ) { ALOGE("ERROR: MediaScanner native registration failed\n" ); goto bail; } if (register_android_media_MediaMetadataRetriever(env) < 0 ) { ALOGE("ERROR: MediaMetadataRetriever native registration failed\n" ); goto bail; } ...... result = JNI_VERSION_1_4; bail: return result; }