赞
踩
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性,但是同时JNI会提升程序的性能。
通常情况下,下面几种情况下才会使用JNI技术:
JNI在Android里的使用很广泛,主要是音视频开发、热修复和插件化、逆向开发、系统源码调用等。涉及到的工具那就是NDK(Native Development Kit)了。
MediaRecorder在Framework中用于录音/录像,其中Java-JNI-Native三层的代码对应关系如下图所示:
进入到源码:frameworks/base/media/java/android/media/MediaRecorder.java,截取一段JNI相关的代码:
public class MediaRecorder
{
//对于Framework层,只要其类中加载了对应的JNI库,在其类中直接声明native方法即可,其余工作会由JNI层完成
static {
System.loadLibrary("media_jni");//用来加载名为libmedia_jni.so的动态库。
native_init();//调用Native方法完成JNI的注册。
}
private final static String TAG = "MediaRecorder";
...省略n行代码...
public native void start() throws IllegalStateException;//native开头的方法代表其为Java类中的一个native方法。
...省略n行代码...
private static native final void native_init();
}
进入到源码:frameworks/base/media/jni/android_media_MediaRecorder.cpp
上一节所述的MediaRecorder.java中的native方法native_init()、start()的JNI层实现就在这里:
//MediaRecorder.java中的native方法native_init()在JNI层的实现 static void android_media_MediaRecorder_native_init(JNIEnv *env) { jclass clazz; clazz = env->FindClass("android/media/MediaRecorder"); if (clazz == NULL) { return; } fields.context = env->GetFieldID(clazz, "mNativeContext", "J"); if (fields.context == NULL) { return; } fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;"); if (fields.surface == NULL) { return; } jclass surface = env->FindClass("android/view/Surface"); if (surface == NULL) { return; } fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V"); if (fields.post_event == NULL) { return; } } //MediaRecorder.java中的native方法start()在JNI层的实现 static void android_media_MediaRecorder_start(JNIEnv *env, jobject thiz) { ALOGV("start"); sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->start(), "java/lang/RuntimeException", "start failed."); }
进入到源码:frameworks/base/media/jni/Android.mk,就能发现这个android_media_MediaRecorder.cpp和同目录下的其它cpp文件共同编译出了动态库 libmedia_jni.so
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ android_media_AmrInputStream.cpp \ android_media_ExifInterface.cpp \ android_media_ImageWriter.cpp \ android_media_ImageReader.cpp \ android_media_MediaCrypto.cpp \ android_media_MediaCodec.cpp \ android_media_MediaCodecList.cpp \ android_media_MediaDataSource.cpp \ android_media_MediaDrm.cpp \ android_media_MediaExtractor.cpp \ android_media_MediaHTTPConnection.cpp \ android_media_MediaMetadataRetriever.cpp \ android_media_MediaMuxer.cpp \ android_media_MediaPlayer.cpp \ android_media_MediaProfiles.cpp \ android_media_MediaRecorder.cpp \ android_media_MediaScanner.cpp \ android_media_MediaSync.cpp \ android_media_ResampleInputStream.cpp \ android_media_SyncParams.cpp \ android_media_Utils.cpp \ android_mtp_MtpDatabase.cpp \ android_mtp_MtpDevice.cpp \ android_mtp_MtpServer.cpp \ LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ libnativehelper \ libutils \ libbinder \ libmedia \ libmediadrm \ libskia \ libui \ liblog \ libcutils \ libgui \ libstagefright \ libstagefright_foundation \ libcamera_client \ libmtp \ libusbhost \ libexif \ libpiex \ libstagefright_amrnb_common LOCAL_STATIC_LIBRARIES := \ libstagefright_amrnbenc LOCAL_C_INCLUDES += \ external/libexif/ \ external/piex/ \ external/tremor/Tremor \ frameworks/base/core/jni \ frameworks/base/libs/hwui \ frameworks/av/media/libmedia \ frameworks/av/media/libstagefright \ frameworks/av/media/libstagefright/codecs/amrnb/enc/src \ frameworks/av/media/libstagefright/codecs/amrnb/common \ frameworks/av/media/libstagefright/codecs/amrnb/common/include \ frameworks/av/media/mtp \ frameworks/native/include/media/openmax \ $(call include-path-for, libhardware)/hardware \ $(PV_INCLUDES) \ $(JNI_H_INCLUDE) LOCAL_CFLAGS += -Wall -Werror -Wno-error=deprecated-declarations -Wunused -Wunreachable-code #在这里编译出libmedia_jni.so LOCAL_MODULE:= libmedia_jni include $(BUILD_SHARED_LIBRARY) # build libsoundpool.so # build libaudioeffect_jni.so include $(call all-makefiles-under,$(LOCAL_PATH))
上一节遗留了个疑问,native_init方法是如何找对自己所对应的android_media_MediaRecorder_native_init方法呢?这就涉及到了本节所述JNI方法的注册问题了。Native方法的注册分为静态注册(常用于应用侧的NDK开发)和动态注册(常用于系统中Framework开发)。
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MediaRecorder_native_1init(JNIEnv *env, jclass clazz) {
// TODO: implement native_init()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MediaRecorder_start(JNIEnv *env, jobject thiz) {
// TODO: implement start()
}
可以看到 native_init 方法声明为 Java_com_example_myapplication_MediaRecorder_native_1init,名字的详细解释如下图所示:
当在Java中调用init_native方法时,Java虚拟机就会去JNI中寻找Java_com_example_myapplication_MediaRecorder_native_1init函数,如果没找到会报错,如果找到了就会为这两者建立关联(通过保存JNI的函数指针,这样在下次调用native_init方法时直接使用这个函数指针即可)
所以,静态注册的关键在于通过方法名将Java方法和JNI函数建立关联。但是虽然静态注册操作简单,适合在Android Studio中写APP时使用,却是有明显缺点的:
在Android系统jni.h中,有专门的数据结构 JNINativeMethod 用来记录Java的Native方法和JNI函数的关联关系。进入到源码:libnativehelper/include/nativehelper/jni.h
typedef struct {
const char* name; //Java方法名
const char* signature; //Java方法名的签名信息
void* fnPtr; //JNI中对应的方法指针
} JNINativeMethod;
依然以MediaRecorder为例,其采用的注册方式就是动态注册。进入到源码:
frameworks/base/media/jni/android_media_MediaRecorder.cpp
static const JNINativeMethod gMethods[] = { {"setCamera", "(Landroid/hardware/Camera;)V", (void *)android_media_MediaRecorder_setCamera}, {"setVideoSource", "(I)V", (void *)android_media_MediaRecorder_setVideoSource}, {"setAudioSource", "(I)V", (void *)android_media_MediaRecorder_setAudioSource}, {"setOutputFormat", "(I)V", (void *)android_media_MediaRecorder_setOutputFormat}, {"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder}, {"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder}, {"setParameter", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameter}, {"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD}, {"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize}, {"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate}, {"setMaxDuration", "(I)V", (void *)android_media_MediaRecorder_setMaxDuration}, {"setMaxFileSize", "(J)V", (void *)android_media_MediaRecorder_setMaxFileSize}, {"_prepare", "()V", (void *)android_media_MediaRecorder_prepare}, {"getSurface", "()Landroid/view/Surface;", (void *)android_media_MediaRecorder_getSurface}, {"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude}, {"start", "()V", (void *)android_media_MediaRecorder_start}, {"stop", "()V", (void *)android_media_MediaRecorder_stop}, {"pause", "()V", (void *)android_media_MediaRecorder_pause}, {"resume", "()V", (void *)android_media_MediaRecorder_resume}, {"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset}, {"release", "()V", (void *)android_media_MediaRecorder_release}, {"native_init", "()V", (void *)android_media_MediaRecorder_native_init}, {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V", (void *)android_media_MediaRecorder_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize}, {"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface }, };
该gMethods[]数组中存放的就是MediaRecorder的Native方法与JNI层函数的对应关系。依然以native_init为例:
注册函数register_android_media_MediaRecorder的功能就是gMethods[]数组中Java方法和JNI函数的对应关系动态注册到系统中。
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaRecorder(JNIEnv *env)
{
// 返回了AndroidRuntime的registerNativeMethods函数;
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaRecorder", gMethods, NELEM(gMethods));
}
注释中说在android_media_MediaPlayer.cpp的JNI_OnLoad函数中会调用register_android_media_MediaRecorder函数,那就先到android_media_MediaPlayer.cpp中去看一下这个JNI_OnLoad函数:
进入到源码:frameworks/base/media/jni/android_media_MediaPlayer.cpp
extern int register_android_media_ExifInterface(JNIEnv *env); extern int register_android_media_ImageReader(JNIEnv *env); extern int register_android_media_ImageWriter(JNIEnv *env); extern int register_android_media_Crypto(JNIEnv *env); extern int register_android_media_Drm(JNIEnv *env); extern int register_android_media_MediaCodec(JNIEnv *env); extern int register_android_media_MediaExtractor(JNIEnv *env); extern int register_android_media_MediaCodecList(JNIEnv *env); extern int register_android_media_MediaHTTPConnection(JNIEnv *env); extern int register_android_media_MediaMetadataRetriever(JNIEnv *env); extern int register_android_media_MediaMuxer(JNIEnv *env); extern int register_android_media_MediaRecorder(JNIEnv *env); extern int register_android_media_MediaScanner(JNIEnv *env); extern int register_android_media_MediaSync(JNIEnv *env); extern int register_android_media_ResampleInputStream(JNIEnv *env); extern int register_android_media_MediaProfiles(JNIEnv *env); extern int register_android_media_AmrInputStream(JNIEnv *env); extern int register_android_mtp_MtpDatabase(JNIEnv *env); extern int register_android_mtp_MtpDevice(JNIEnv *env); extern int register_android_mtp_MtpServer(JNIEnv *env); jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { 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_ImageWriter(env) != JNI_OK) { ALOGE("ERROR: ImageWriter native registration failed"); goto bail; } if (register_android_media_ImageReader(env) < 0) { ALOGE("ERROR: ImageReader native registration failed"); goto bail; } // 调用register_android_media_MediaPlayer函数; if (register_android_media_MediaPlayer(env) < 0) { ALOGE("ERROR: MediaPlayer native registration failed\n"); goto bail; } // 调用register_android_media_MediaRecorder函数; 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; } if (register_android_media_AmrInputStream(env) < 0) { ALOGE("ERROR: AmrInputStream native registration failed\n"); goto bail; } if (register_android_media_ResampleInputStream(env) < 0) { ALOGE("ERROR: ResampleInputStream native registration failed\n"); goto bail; if (register_android_media_MediaProfiles(env) < 0) { ALOGE("ERROR: MediaProfiles native registration failed"); goto bail; } if (register_android_mtp_MtpDatabase(env) < 0) { ALOGE("ERROR: MtpDatabase native registration failed"); goto bail; } if (register_android_mtp_MtpDevice(env) < 0) { ALOGE("ERROR: MtpDevice native registration failed"); goto bail; } if (register_android_mtp_MtpServer(env) < 0) { ALOGE("ERROR: MtpServer native registration failed"); goto bail; } if (register_android_media_MediaCodec(env) < 0) { ALOGE("ERROR: MediaCodec native registration failed"); goto bail; } if (register_android_media_MediaSync(env) < 0) { ALOGE("ERROR: MediaSync native registration failed"); goto bail; } if (register_android_media_MediaExtractor(env) < 0) { ALOGE("ERROR: MediaCodec native registration failed"); goto bail; } if (register_android_media_MediaMuxer(env) < 0) { ALOGE("ERROR: MediaMuxer native registration failed"); goto bail; } if (register_android_media_MediaCodecList(env) < 0) { ALOGE("ERROR: MediaCodec native registration failed"); goto bail; } if (register_android_media_Crypto(env) < 0) { ALOGE("ERROR: MediaCodec native registration failed"); goto bail; } if (register_android_media_Drm(env) < 0) { ALOGE("ERROR: MediaDrm native registration failed"); goto bail; } if (register_android_media_MediaHTTPConnection(env) < 0) { ALOGE("ERROR: MediaHTTPConnection native registration failed"); goto bail; } if (register_android_media_ExifInterface(env) < 0) { ALOGE("ERROR: ExifInterface native registration failed"); goto bail; } /* success -- return valid version number */ result = JNI_VERSION_1_4; bail: return result; }
发现多媒体框架中有很多框架都需要进行JNINativeMethod数组的注册,所以,统一在android_media_MediaPlayer.cpp中的JNI_OnLoad函数中,而JNI_OnLoad的调用会在System.loadLibrary函数调用之后被调用。
追踪一下register_android_media_MediaRecorder函数实际调用的是frameworks/base/core/jni/AndroidRuntime.cpp中的AndroidRuntime::registerNativeMethods函数:
/*
* 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);
}
AndroidRuntime::registerNativeMethods 函数中又调用了帮助类 libnativehelper/JNIHelp.cpp 中的jniRegisterNativeMethods函数。
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) { JNIEnv* e = reinterpret_cast<JNIEnv*>(env); ALOGV("Registering %s's %d native methods...", className, numMethods); scoped_local_ref<jclass> c(env, findClass(env, className)); if (c.get() == NULL) { char* tmp; const char* msg; if (asprintf(&tmp, "Native registration unable to find class '%s'; aborting...", className) == -1) { // Allocation failed, print default warning. msg = "Native registration unable to find class; aborting..."; } else { msg = tmp; } e->FatalError(msg); } //最终调用了JNIEnv的RegisterNatives函数完成JNI的注册!!! if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { char* tmp; const char* msg; if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) { // Allocation failed, print default warning. msg = "RegisterNatives failed; aborting..."; } else { msg = tmp; } e->FatalError(msg); } return 0;
JNI的动态注册最终调用了JNIEnv的RegisterNatives函数去完成JNI的动态注册!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。