Androidframework层JNI的使用浅析
发表时间:2020-10-19
发布人:葵宇科技
浏览次数:77
尊敬原创:http://blog.csdn.net/yuanzeyao/article/details/42418977
JNI技巧对于多java开辟的同伙信赖并不陌生,即(java native interface),本地调用接口,重要功能有以下两点:
1、java层调用C/C++层代码
2、C/C++层调用java层代码
可能有些人会认为jni技巧破坏了Java说话的跨平台性,有这种设法主意可能是因为你对java懂得得还不敷深,如不雅你看看jdk源码,你会发明在jdk琅绫擎大年夜量应用了jni技巧,并且java虚拟机就是用本地说话写的,所以导致jvm并不克不及跨平台性,所以说java的跨平台性并不是100%的跨平台的。相反你应当看到应用Jni的优势:
1、因为C/C++说话本来机比java说话出生早,所以很多库代码都是应用C/C++写的,有了Jni我们就可以直接应用了,不消反复造轮子。
2、弗成否定,C/C++履行效力比java 高,对于一些对效力有请求的功能,必须应用C/C++.
因为盘似揭捉究Android 中java层和native层是若何连接起来的,所以想研究一下Android中的jni技巧(在浏览之前,最好懂得jni中的根本常识,如jni中数据类型,签名格局,不然看起来可能有些吃力),因为工作和MediaPlayer有关,这里就应用MediaPlayer为例吧。
下面给出一张图,经由过程此图,我们扼要解释一下jni是若何连接Java层和本地层的。
[img]http://img.blog.csdn.net/20150105104601352?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnpleWFv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
当我们的app要播放视频的时刻,我们应用的是java层的MediaPlayer类,我们进入到MediaPlayer.java看看(提示:我这里应用的是源码4.1)
重要留意的有两点:
1、静态代码块:
static {
System.loadLibrary("media_jni");
native_init();
}2、native_init的签名:
private static native final void native_init();
看到静态代码块后,我们可以知道MediaPlayer对应的jni层代码在Media_jni.so库中
本地层对应的so库是libmedia.so,所以MediaPlayer.java经由过程Media_jni.so和MediaPlayer.cpp(libmedia.so)进行交互
下面我们就深刻到细节吧。不过在深刻细节前,我先要告诉你一个规矩,在Android中,平日java层类和jni层类的名字有如下关系,拿MediaPlayer为例,java层叫android.media.MediaPlayer.java,那么jni层叫做android_media_MediaPlayer.cpp
因为native_init是一个本处所法,那么我们就到android_media_MediaPlayer.cpp找到native_init的对应办法吧
static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
if (clazz == NULL) {
return;
}
fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
if (fields.context == NULL) {
return;
}
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
return;
}
fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "I");
if (fields.surface_texture == NULL) {
return;
}
}对应膳绫擎的代码,如不雅你对java中的反射懂得得很透辟的话,其实很好懂得,起首找到java层的MediaPlayer的Class对象,jclass是java层Class在native层的代码,然后分别保存mNaviceContext字段,postEventFromNative办法,mNativeSurfaceTexture字段。
其拭魅这里我最想解释的是别的一个问题,就是MediaPlayer中的native_init办法时若何跟android_media_MediaPlayer.cpp中的android_media_MediaPlayer_native_init对应起来的,因为我们知道如不雅应用javah主动生成的头文件,那么在jni层的名字应当是java_android_media_MediaPlayer_native_linit。其拭魅这里涉及到一个动态注册的过程。
其实袈溱java层代用System.loadLibrary成功后,就会调用jni文件中的JNI_onLoad办法,android_media_MediaPlayer.cpp中的JNI_onLoad办法如下(朝长进步部分)
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_MediaPlayer(env) < 0) {
ALOGE("ERROR: MediaPlayer native registration failed\n");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail:
return result;
}这里有一个办法叫做register_android_media_MediaPlayer,我们进入此办法,看看注册了什么
static int register_android_media_MediaPlayer(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}这里就是调用了AndroidRuntime供给的registerNativeMethods办法,这里涉及到一个gMethods的变量,它其实是一个构造体
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;name:就是在java层办法名称
signature:就是办法在签名
fnPtr:在jni层对应的函数名称
,那么我们找到native_init在gMethods对应的值吧
static JNINativeMethod gMethods[] = {
{
"_setDataSource",
"(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
(void *)android_media_MediaPlayer_setDataSourceAndHeaders
},
....
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
...
};接下来,我们看看AndroidRuntime中的registerNativeMethods做了什么吧
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods)
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
调用了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 natives", className);
scoped_local_ref<jclass> c(env, findClass(env, className));
if (c.get() == NULL) {
ALOGE("Native registration unable to find class '%s', aborting", className);
abort();
}
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
ALOGE("RegisterNatives failed for '%s', aborting", className);
abort();
}
return 0;
}最终调用了env的RegisterNativers完成了注册。
其实写到这里,我们已经知道了java层和jni是若何接洽起来的,接下来我想说的是jni是若何将java层和native接洽起来的,照样用MediaPlayer为例吧,我们进入MediaPlayer的构造函数。
public MediaPlayer() {
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
native_setup(new WeakReference<MediaPlayer>(this));
}这里创建了一个mEventHandler对象,并调用了native_setup办法,我们进入到android_media_MediaPlayer.cpp的对应办法看看
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
ALOGV("native_setup");
sp<MediaPlayer> mp = new MediaPlayer();
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
}
// create new listener and give it to MediaPlayer
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
mp->setListener(listener);
// Stow our new C++ MediaPlayer in an opaque field in the Java object.
setMediaPlayer(env, thiz, mp);
}这里创建了一个本地MediaPlayer对象,并且设置了listener,(如不雅做过播放器的同窗应当知道这个listener应当知道干啥,不知道也没紧要),最后调用了setMediaPlayer办法,这个才是我们须要存眷的。
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
Mutex::Autolock l(sLock);
sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
if (player.get()) {
player->incStrong(thiz);
}
if (old != 0) {
old->decStrong(thiz);
}
env->SetIntField(thiz, fields.context, (int)player.get());
return old;
}其实就是先拿到fields.context的对应的值,还记得这个这个值是什么吗,不记得的可以回到膳绫擎看看
fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
其实就是java层mNativeContext对应的值,就是将本地MediaPlayer的地址存放到mNativeContext中。
如今参加我们要播放一个本地Mp4视频,那么应用如下代码即可
mediaPlayer.setDataSource("/mnt/sdcard/a.mp4");
mediaPlayer.setDisplay(surface1.getHolder());
mediaPlayer.prepare();
mediaPlayer.start(); 其拭魅这里调用的 几个都是本处所法,这里我就是用prepare办法为例,讲解MediaPlaeyr.java和MediaPlayer.cpp的交互
当在java层调用prepare办法时,在jni层会调用如下办法
static void
android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return;
}
// Handle the case where the display surface was set before the mp was
// initialized. We try again to make it stick.
sp<ISurfaceTexture> st = getVideoSurfaceTexture(env, thiz);
mp->setVideoSurfaceTexture(st);
process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
}这里经由过程getMediaPlayer办法拿到本地的MediaPlayer对象,调用调用本处所法process_media_player_call,并将本地MediaPlayer调用parepare办法的结不雅传递给此办法。
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
if (exception == NULL) { // Don't throw exception. Instead, send an event.
if (opStatus != (status_t) OK) {
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
}
} else { // Throw exception!
if ( opStatus == (status_t) INVALID_OPERATION ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
} else if ( opStatus == (status_t) PERMISSION_DENIED ) {
jniThrowException(env, "java/lang/SecurityException", NULL);
} else if ( opStatus != (status_t) OK ) {
if (strlen(message) > 230) {
// if the message is too long, don't bother displaying the status code
jniThrowException( env, exception, message);
} else {
char msg[256];
// append the status code to the message
sprintf(msg, "%s: status=0x%X", message, opStatus);
jniThrowException( env, exception, msg);
}
}
}
}在这个琅绫擎根据prepare返回的状况,如不雅exception==null 并且prepare履行掉败,测试不抛异常,而是调用本地MediaPlayer的notify办法。
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
bool send = true;
bool locked = false;
...
switch (msg) {
case MEDIA_NOP: // interface test message
break;
case MEDIA_PREPARED:
ALOGV("prepared");
mCurrentState = MEDIA_PLAYER_PREPARED;
if (mPrepareSync) {
ALOGV("signal application thread");
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mSignal.signal();
}
break;
case MEDIA_PLAYBACK_COMPLETE:
ALOGV("playback complete");
if (mCurrentState == MEDIA_PLAYER_IDLE) {
ALOGE("playback complete in idle state");
}
if (!mLoop) {
mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
}
break;
case MEDIA_ERROR:
// Always log errors.
// ext1: Media framework error code.
// ext2: Implementation dependant error code.
ALOGE("error (%d, %d)", ext1, ext2);
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
if (mPrepareSync)
{
ALOGV("signal application thread");
mPrepareSync = false;
mPrepareStatus = ext1;
mSignal.signal();
send = false;
}
break;
case MEDIA_INFO:
// ext1: Media framework error code.
// ext2: Implementation dependant error code.
if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
ALOGW("info/warning (%d, %d)", ext1, ext2);
}
break;
case MEDIA_SEEK_COMPLETE:
ALOGV("Received seek complete");
if (mSeekPosition != mCurrentPosition) {
ALOGV("Executing queued seekTo(%d)", mSeekPosition);
mSeekPosition = -1;
seekTo_l(mCurrentPosition);
}
else {
ALOGV("All seeks complete - return to regularly scheduled program");
mCurrentPosition = mSeekPosition = -1;
}
break;
case MEDIA_BUFFERING_UPDATE:
ALOGV("buffering %d", ext1);
break;
case MEDIA_SET_VIDEO_SIZE:
ALOGV("New video size %d x %d", ext1, ext2);
mVideoWidth = ext1;
mVideoHeight = ext2;
break;
case MEDIA_TIMED_TEXT:
ALOGV("Received timed text message");
break;
default:
ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
break;
}
sp<MediaPlayerListener> listener = mListener;
if (locked) mLock.unlock();
// this prevents re-entrant calls into client code
if ((listener != 0) && send) {
Mutex::Autolock _l(mNotifyLock);
ALOGV("callback application");
listener->notify(msg, ext1, ext2, obj);
ALOGV("back from callback");
}
}
做过播放器的同窗应当对膳绫擎几个消息都不陌生吧,因为刚才调用prepare办法掉败了,所以这里应当履行MEDIA_ERROR分支,最后调用listener的notify代码,这个listener就是在native_setup中设置的
void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
jobject jParcel = createJavaParcelObject(env);
if (jParcel != NULL) {
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
nativeParcel->setData(obj->data(), obj->dataSize());
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, jParcel);
}
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
msg, ext1, ext2, NULL);
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
LOGW_EX(env);
env->ExceptionClear();
}
}
还记得fields.post_event保存的是什么吗
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");就是java层MediaPlayer的postEventFromNative办法,也就是说如不雅播放掉足了,那么就经由过程调用postEventFromNative办法来告诉java层的MediaPlayer。
private static void postEventFromNative(Object mediaplayer_ref,
int what, int arg1, int arg2, Object obj)
{
MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
if (mp == null) {
return;
}
if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
// this acquires the wakelock if needed, and sets the client side state
mp.start();
}
if (mp.mEventHandler != null) {
Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
mp.mEventHandler.sendMessage(m);
}
}这个时光最终经由过程mEventHandler处理,也就是在我们app过程中处理这个缺点。
写到这里,信赖你应当对java层和native层的交淮竽暌剐了导致的懂得。








