安卓MediaPlayer源码跟读解析

简介:

        安卓通过MediaPlayer这个类提供了一整套接口呈现给给客户实现视音频的播放。可是凡事必究其根,我们今天就来看看安卓的MediaPlayer框架(基于Android 8.0)究竟是怎么实现的。因为框架层全是C/C++代码,建议读者拥有相关基础,没有也没关系,都能看懂。

概要: 

        先给出网上扒的MediaPlayer状态图,MediaPlayer常用的方法也就这些了。

        

        因为音视频相关的编解码,解封装,渲染等操作需要大量的运算,所以谷歌将这些方法通过底层C/C++代码来实现了。但是java怎么调用的C++代码呢?于是JNI(java native interface)就应运而生了。JNI允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。

        因此我们可以给出MediaPlayer框架的层次架构图:

         代码分析:

        我们写java程序如果要用到视音频播放肯定少不了MediaPlayer,常见的调用方法大致如下:

mediaPlayer = MediaPlayer.create(MainActivity.this, R.raw.test);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.setLooping(true);
mediaPlayer.start();

         是不是很熟悉?先通过creat()函数创建一个mediaPlayer,再设置音频流类型,设计播放窗口,就start(),一般都是固定的几步,可是它的底层是怎么运作的呢?我们现在来分析一下。就看creat()函数吧,跟着它往下读。

creat()

         当然先去MediaPlayer.java里找一找了。

         目录:xref: /frameworks/base/media/java/android/media/MediaPlayer.java

   //往下调用
   public static MediaPlayer create(Context context, int resid) {
        int s = AudioSystem.newAudioSessionId();
        return create(context, resid, null, s > 0 ? s : 0);
    }
   //还是要预先创建一个自己,然后调用setDataSource()函数
   public static MediaPlayer create(Context context, int resid,
            AudioAttributes audioAttributes, int audioSessionId) {
        try {
            AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
            if (afd == null) return null;

            MediaPlayer mp = new MediaPlayer();

            final AudioAttributes aa = audioAttributes != null ? audioAttributes :
                new AudioAttributes.Builder().build();
            mp.setAudioAttributes(aa);
            mp.setAudioSessionId(audioSessionId);

            mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
            afd.close();
            mp.prepare();
            return mp;
        } catch (IOException ex) {
            Log.d(TAG, "create failed:", ex);
            // fall through
        } catch (IllegalArgumentException ex) {
            Log.d(TAG, "create failed:", ex);
           // fall through
        } catch (SecurityException ex) {
            Log.d(TAG, "create failed:", ex);
            // fall through
        }
        return null;
    }

    public void setDataSource(FileDescriptor fd, long offset, long length)
            throws IOException, IllegalArgumentException, IllegalStateException {
        _setDataSource(fd, offset, length);
    }

    private native void _setDataSource(FileDescriptor fd, long offset, long length)
            throws IOException, IllegalArgumentException, IllegalStateException;

          我把这几个相关的函数穿了起来,层层调用。java对上层那么多的重载函数,在底层都是互相调用,最终归为一两个特别的函数上。还记得初学java的MediaPlayer时,网上说可以new一个MediaPlayer也可以不new,直接调用creat()函数,我觉得不可思议,现在算是真相大白了。creat()函数在内部先new了一个MediaPlayer来处理业务,然后调用MediaPlayer的setDataSource()函数来设置播放资源,setDataSource()函数是我们研究框架的开路先锋,他是最重要的一个函数了。注意最后一个函数的native标志,它标示了这是一个底层函数的声明,你在当前目录是找不到它的定义的,这就是JNI。

         但是看native层的代码之前,我们注意到在MediaPlayer.java的构造函数之前有一段静态代码段:

static {
        System.loadLibrary("media_jni");
        native_init();
    }

private static native final void native_init();

        嗯?提前调用了native层的native_init方法。没完呢,我们注意到在MediaPlayer.java的构造函数里也有一段native代码:

 native_setup(new WeakReference<MediaPlayer>(this));

 private native final void native_setup(Object mediaplayer_this);

        这两个native函数在new一个MediaPlayer时就会调用,别放过,很重要,我们马上就看看。

JNI:

        JNI的全称就是Java Native Interface,就是Java和C/C++相互通信的接口,实现了一个工程,多种语言并存。那么什么时候需要用到JNI呢?

        1.需要调用java语言不支持的依赖于操作系统平台的特性的一些功能;

        2.为了整合一些以前使用非java语言开发的某些系统,例如C和C++;

        3.为了节省程序的运行时间,必须采用一些低级或中级语言。

       关于JNI的用法请看:JNI的用法

       MediaPlayer.java调用了底层JNI,根据JNI特有的命名规则,我们要找android_media_MediaPlayer.cpp文件。好了我们上面看到java层调用了native层的函数,有native_init(),native_setup(),_setDataSource(),然后我们兴高采烈的在android_media_MediaPlayer.cpp文件中并没有找到这三个函数的定义,什么情况?

        不慌,细心的人就会发现JNI有这么一块代码,实现了对函数换名!

 xref: /frameworks/base/media/jni/android_media_MediaPlayer.cpp 

static const JNINativeMethod gMethods[] = {
    {
        "nativeSetDataSource",
        "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
        "[Ljava/lang/String;)V",
        (void *)android_media_MediaPlayer_setDataSourceAndHeaders
    },

    {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值