用户在播放音效的时候,偶现ANR现象,友盟上传上来的log如下
可以发现,在执行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();
}
});
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END