阅读 748

Mediaplayer.prepare()导致的ANR问题

用户在播放音效的时候,偶现ANR现象,友盟上传上来的log如下

image.png

可以发现,在执行Mediaplayer.prepare方法的时候ANR了

原因分析:

Mediaplayer有两个准备资源的方法,一个是prepare,还有一个prepareAsync方法,这两个方法的区别是,一个是同步的,一个是异步的

我们看一下prepare和prepareAsync的源码(C++层)

status_t MediaPlayer::prepare()
{
    ALOGV("prepare");
    Mutex::Autolock _l(mLock);
    mLockThreadId = getThreadId();
    if (mPrepareSync) {
        mLockThreadId = 0;
        return -EALREADY;
    }
    mPrepareSync = true;
    status_t ret = prepareAsync_l();
    if (ret != NO_ERROR) {
        mLockThreadId = 0;
        return ret;
    }

    if (mPrepareSync) {
        mSignal.wait(mLock);  // wait for prepare done
        mPrepareSync = false;
    }
    ALOGV("prepare complete - status=%d", mPrepareStatus);
    mLockThreadId = 0;
    return mPrepareStatus;
}
status_t MediaPlayer::prepareAsync()
{
    ALOGV("prepareAsync");
    Mutex::Autolock _l(mLock);
    return prepareAsync_l();
}
复制代码

从源码我们可以看出,不管是prepare和prepareAsync方法,最终都会调用到prepareAsync_l(),但是prepare多了一段

  if (mPrepareSync) {
        mSignal.wait(mLock);  // wait for prepare done
        mPrepareSync = false;
    }
复制代码

这里调用了wait方法使播放线程等待资源的准备,所以使得java层达到了同步调用的效果,然后在prepare完成之后会调用notify方法唤醒播放线程,代码如下

  void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
    {
        ...
        case MEDIA_PREPARED:
            ALOGV("prepared");
            mCurrentState = MEDIA_PLAYER_PREPARED;
            if (mPrepareSync) {
                ALOGV("signal application thread");
                mPrepareSync = false;
                mPrepareStatus = NO_ERROR;
                mSignal.signal();
            }
            break;
    }
复制代码

通过看源码,果然可以确定是因为prepare方法类似直接在当前线程去读取资源,即使资源文件是一个网络资源,当网络条件比较差即弱网情况下时,那么发生ANR的几率就会十分高了,而且如果请求中断或者文件不完整,也会导致播放失败,解决方法之一的话可以采用下面的方式去播放一个语音

  MediaPlayer mediaPlayer = new MediaPlayer();
       mediaPlayer.setDataSource(url);
       mediaPlayer.prepareAsync();
       mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
           @Override
           public void onPrepared(MediaPlayer mp) {
               mp.start();
           }
       });
复制代码
文章分类
Android
文章标签