目录
前言
之前的《》只是介绍了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中的,这个后面继续分析。
参考文章
《》
《》
《》
《》