目录
前言
之前的《》只是介绍了java中的常用方法,对于JNI层的调用没有进一步介绍,今天就介绍一下JNI层。
个人流水账哈,推荐看其他人的,我这只是自己的跟踪记录
涉及的代码:
frameworks\base\media\java\android\media\MediaPlayer.java frameworks\base\media\jni\android_media_MediaPlayer.cpp frameworks\base\core\jni\AndroidRuntime.cpp frameworks\av\media\libmedia\mediaplayer.cpp libnativehelper\include\nativehelper\JNIHelp.h frameworks\av\media\libmedia\IMediaPlayer.cpp frameworks\av\media\libmedia\IMediaDeathNotifier.cpp frameworks\native\libs\binder\IServiceManager.cpp frameworks\av\media\libmedia\IMediaPlayerService.cpp
正文
MediaPlayer.java
接上次demo调用的方法,主要涉及的本地方法,本文主要是按照下面的顺序去看代码的:
1. System.loadLibrary("media_jni"); 2. native_init(); 3. native_setup(); 4. _setDataSource(); 5. _prepare(); 6. _start();
当然还有其他方法,都类似。
android_media_MediaPlayer.cpp
按照以前学习的知识,JNI对应的代码是以包名+类名命名,也就是android_media_MediaPlayer.cpp。
loadLibrary("media_jni")
动态注册,会调用JNI_OnLoad(),具体就不解释了。
我们关注点
static int register_android_media_MediaPlayer(JNIEnv *env){ return AndroidRuntime::registerNativeMethods(env, "android/media/MediaPlayer", gMethods, NELEM(gMethods)); }
int AndroidRuntime::registerNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods){ return jniRegisterNativeMethods(env, className, gMethods, numMethods); }
jniRegisterNativeMethods是jniRegisterNativeMethods函数是Android平台提供的帮助函数,可看:
libnativehelper\include\nativehelper\JNIHelp.h
gMethods中可以看对应实现的方法
//部分 static const JNINativeMethod gMethods[] = { {"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, {"native_init", "()V", (void *)android_media_MediaPlayer_native_init}, {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, };
native_init
从上面gMethods可以知道
static void android_media_MediaPlayer_native_init(JNIEnv *env){ jclass clazz; //加载android.media.MediaPlayer clazz = env->FindClass("android/media/MediaPlayer"); //略 // postEventFromNative java方法ID,用于回调 fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/Object;IIILjava/lang/Object;)V"); if (fields.post_event == NULL) { return; } //释放 env->DeleteLocalRef(clazz); //略 }
有个重点,获取了Java方法的MethodID,后面可以调用postEventFromNative(),下面会介绍到。
native_setup()
static void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this){ //创建MediaPlayer对象 sp<MediaPlayer> mp = new MediaPlayer(); //回调监听,调用Java方法postEventFromNative回调 sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this); mp->setListener(listener); //把MediaPlayer对象保存起来了,后面会通过get获取 setMediaPlayer(env, thiz, mp); }
MediaPlayer()后面看。
JNIMediaPlayerListener
JNIMediaPlayerListener类中有个notify()方法
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); env->DeleteLocalRef(jParcel); } } else { //重点 env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, NULL); } }
很明显,这里调用了java中的postEventFromNative方法,fields.post_event就是在native_init()获取的MedthoID。
CallStaticVoidMethod是jni.h中定义的,还有很多类似的方法,比如:
CallStaticDoubleMethod() CallStaticIntMethod() CallStaticBooleanMethodA()
根据Java方法回调的参数和属性进行调用不同的方法。
具体可看android-ndk-r21d-windows-x86_64[这个我下载的的NDK版本]中找jni.h文件,里面有很多JNI相关的定义。
setMediaPlayer
用于保存MediaPlayer对象
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz){ Mutex::Autolock l(sLock);//锁 //获取已经初始化过的MediaPlayer MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context); return sp<MediaPlayer>(p); } static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player){ Mutex::Autolock l(sLock); sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context); if (player.get()) { player->incStrong((void*)setMediaPlayer); } if (old != 0) { old->decStrong((void*)setMediaPlayer); } //保存MediaPlayer,后面会获取 env->SetLongField(thiz, fields.context, (jlong)player.get()); return old; }
_setDataSource()
_setDataSource在MediaPlayer.java中有两个方法
private native void _setDataSource(FileDescriptor fd, long offset, long length); private native void _setDataSource(MediaDataSource dataSource);
因此在android_media_MediaPlayer.cpp也有两个,只不过两个传的参数不一样。看我们demo中调用的
static void android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length){ //获取MediaPlayer对象 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); //也在上面nativehelper\JNIHelp.h定义,这里不关心 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); //重点mp->setDataSource(fd, offset, length) //process_media_player_call只是处理状态码,后面很多方法都用这个。 process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); }
进入mediaplayer.cpp中的setDataSource()啦,暂时跳过,后面看。
prepare()
static void android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz){ //获取MediaPlayer对象 sp<MediaPlayer> mp = getMediaPlayer(env, thiz); //mp->prepare() process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." ); }
最终也是mp->prepare(),这里暂时跳过。
_start()
static void android_media_MediaPlayer_start(JNIEnv *env, jobject thiz){ sp<MediaPlayer> mp = getMediaPlayer(env, thiz); //mp->start() process_media_player_call( env, thiz, mp->start(), NULL, NULL ); }
最终也是 mp->start()。
到此,其他方法都差不多,最终也是MediaPlayer类[mediaplayer.cpp]的调用。
mediaplayer.cpp
这里进入mediaplayer.cpp啦,然后根据上面进入的方法走一下。
接上面native_setup()中
sp<MediaPlayer> mp = new MediaPlayer();
初始化mp对象,构造函数中就是一些默认值的初始化,这里就不附上了。
native_setup()中传入了
sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this); mp->setListener(listener);
传入JNIMediaPlayerListener,我们知道JNIMediaPlayerListener.notify()【上面有介绍】会调用postEventFromNative()方法,用于返回播放状态和状态等信息。
setDataSource()
status_t MediaPlayer::setDataSource( const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers){ status_t err = BAD_VALUE; if (url != NULL) { //[重点]。BpMediaPlayerService的对象service const sp<IMediaPlayerService> service(getMediaPlayerService()); if (service != 0) { //[重点]。创建BpMediaPlayer对象player sp<IMediaPlayer> player(service->create(this, mAudioSessionId)); if ((NO_ERROR != doSetRetransmitEndpoint(player)) || //BpMediaPlayer.setDataSource(); (NO_ERROR != player->setDataSource(httpService, url, headers))) { player.clear(); } err = attachNewPlayer(player); } } return err; }
为啥说service是BpMediaPlayerService的对象?
getMediaPlayerService()
const sp<IMediaPlayerService>IMediaDeathNotifier::getMediaPlayerService() { Mutex::Autolock _l(sServiceLock); //单例模式,如果是第一次,sMediaPlayerService==0 if (sMediaPlayerService == 0) { //interface_cast获取BpMediaPlayerService的对象 sMediaPlayerService = interface_cast<IMediaPlayerService>(binder); } return sMediaPlayerService; }
至于interface_cast,推荐看《》
为啥说player是BpMediaPlayer
virtual sp<IMediaPlayer> create( const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); data.writeStrongBinder(IInterface::asBinder(client)); data.writeInt32(audioSessionId); remote()->transact(CREATE, data, &reply); //interface_cast获取BpMediaPlayer return interface_cast<IMediaPlayer>(reply.readStrongBinder()); }
最后调用的是BpMediaPlayer的setDataSource()
status_t setDataSource(int fd, int64_t offset, int64_t length) { Parcel data, reply; data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); data.writeFileDescriptor(fd); data.writeInt64(offset); data.writeInt64(length); //有点晕,BpBinder(0) remote()->transact(SET_DATA_SOURCE_FD, data, &reply); return reply.readInt32(); }
PS: 至于remote()这个是谁,是BpBinder(0)?我目前有点晕了。。。后续补上
最终真正处理,也不是mediaplayer.cpp,,,好绕啊。今天不展开了。等下集。
prepare()
//prepare() status_t MediaPlayer::prepare(){ Mutex::Autolock _l(mLock); mLockThreadId = getThreadId(); if (mPrepareSync) { mLockThreadId = 0; return -EALREADY; } mPrepareSync = true; //最终都是走了prepareAsync_l() status_t ret = prepareAsync_l(); if (ret != NO_ERROR) { mLockThreadId = 0; return ret; } //[重点] 等待Prepare完成,也就是等mSignal释放锁, //这里跟prepareAsync()的区别 if (mPrepareSync) { mSignal.wait(mLock); mPrepareSync = false; } mLockThreadId = 0; return mPrepareStatus; } //prepareAsync() status_t MediaPlayer::prepareAsync(){ Mutex::Autolock _l(mLock); //最终都是走了prepareAsync_l() return prepareAsync_l(); }
或许你会问题 mSignal.wait(mLock)啥时候等到呢?具体看
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj){ bool send = true; bool locked = false; if (mLockThreadId != getThreadId()) { mLock.lock(); locked = true; } switch (msg) { case MEDIA_PREPARED: mCurrentState = MEDIA_PLAYER_PREPARED; if (mPrepareSync) { mPrepareSync = false; mPrepareStatus = NO_ERROR; mSignal.signal(); } break; case MEDIA_ERROR: mCurrentState = MEDIA_PLAYER_STATE_ERROR; if (mPrepareSync){ mPrepareSync = false; mPrepareStatus = ext1; mSignal.signal(); send = false; } break; } sp<MediaPlayerListener> listener = mListener; if (locked) mLock.unlock(); if ((listener != 0) && send) { Mutex::Autolock _l(mNotifyLock); // 这个就是我们传入的JNIMediaPlayerListener listener->notify(msg, ext1, ext2, obj); } }
prepareAsync()和prepare()都调用prepareAsync_l()
status_t MediaPlayer::prepareAsync_l(){ if ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) { //执行BpMediaPlayer.prepareAsync() return mPlayer->prepareAsync(); } //有个短暂的PREPARING状态 mCurrentState = MEDIA_PLAYER_PREPARING; return INVALID_OPERATION; }
BpMediaPlayer.prepareAsync()中也是调用远程的服务,这里暂不深入。
start()
status_t MediaPlayer::start(){ status_t ret = NO_ERROR; Mutex::Autolock _l(mLock); mLockThreadId = getThreadId(); if (mCurrentState & MEDIA_PLAYER_STARTED) { ret = NO_ERROR; } else if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) { //设置是否循环mLoop mPlayer->setLooping(mLoop); //设置音量 mPlayer->setVolume(mLeftVolume, mRightVolume); //这个不太懂。。。一般APP没设置过 mPlayer->setAuxEffectSendLevel(mSendLevel); //改变播放状态 mCurrentState = MEDIA_PLAYER_STARTED; //BpMediaPlayer.prepareAsync() ret = mPlayer->start(); if (ret != NO_ERROR) { mCurrentState = MEDIA_PLAYER_STATE_ERROR; } } else { ret = INVALID_OPERATION; } mLockThreadId = 0; return ret; }
提前设置配置部分配置,不过最终还是调用BpMediaPlayer中的方法。
对于其他的方法,其实也是调用BpMediaPlayer中的,这个后面继续分析。
参考文章
-
《》
-
《》
-
《》
-
《》