Android的MediaPlayer的复位冻结UI [英] Android MediaPlayer reset freezes UI

查看:181
本文介绍了Android的MediaPlayer的复位冻结UI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的Andr​​oid 的MediaPlayer 的问题改变了玩家的数据源时。据该的MediaPlayer (的规格<一href="http://developer.android.com/reference/android/media/MediaPlayer.html">http://developer.android.com/reference/android/media/MediaPlayer.html)我必须重置修改数据源。这工作得很好,但是当玩家只要 channelChanged 方法被调用两次快速连续的 MediaPlayer.reset 冻结UI我的配置文件code因为在这里看到:

 公共无效channelChanged(字符串streamingUrl)
{
    长M1 = System.currentTimeMillis的();
    mMediaPlayer.reset();
    长M2 = System.currentTimeMillis的();
    尝试
    {
        mMediaPlayer.setDataSource(streamingUrl);
    }
    赶上(IOException异常E)
    {
        e.printStackTrace();
    }
    长M3 = System.currentTimeMillis的();
    。mMediaPlayer prepareAsync();
    长M4 = System.currentTimeMillis的();
    Log.d(MEDIAPLAYER,复位+(M2  -  M1));
    Log.d(MEDIAPLAYER,的setDataSource:+(立方米 - 平方米));
    Log.d(MEDIAPLAYER,preparing:+(M4  - 立方米));
}
 

  

复位:3

     

的setDataSource:1

     

preparing:0

     

复位:3119

     

的setDataSource:2

     

preparing:1

所以显然重置被阻止的异步preparing 第一次调用(当我等待直到第一个流开始,然后调用 channelChanged()再次,一切都很好)。

任何想法如何解决这个问题呢?我应该在一个单独的线程执行整个方法?基本上,我想避免这种情况,因为这似乎不是一个良好的编码风格,都不可能导致一些其他问题,如:当用户试图再次启动播放器,但玩家仍然在重置方法,它在另一方面,似乎等待异步prepare 方法。目前尚不清楚如何将球员的表现......

有没有其他好的解决办法?

解决方案

MediaPlayer的是一个棘手的私生子。我建议你​​看一看示例应用程序,其中的MediaPlayer糟糕的设计是由显而易见通过查看$ C $的烂摊子c您必须围绕它写有一个一致的媒体播放体验。

如果有的话,在看样后,您会看到,当他们想跳过曲目,他们本质上的重置并释放...

  mPlayer.reset();
    mPlayer.release();
 

......后来当他们准备加载新的轨道......

 尝试{
          mPlayer.reset();
          mPlayer.setDataSource(someUrl);
          mPlayer.setOn preparedListener(新MediaPlayer.On preparedListener(){
             @覆盖
              在prepared公共无效(MediaPlayer的媒体播放器){
                   //咣!
              }
          });
          。MPLAYER prepareAsync();
    }赶上(IllegalStateException异常E){
        e.printStackTrace();
    }赶上(IOException异常E){
        e.printStackTrace();
    }赶上(抛出:IllegalArgumentException E){
        e.printStackTrace();
    }
 

我加了try / catch语句,因为在某些设备/操作系统版本,MediaPlayer正在比别人差,有时它只是做怪异的东西。你应该有一个接口/监听器,它能够反应这些情况的

更新

这是我用的,当我停止(或暂停),我的音乐播放的方法(大部分是取自示例应用程序,这是一个服务运行,它已被修改,以适应自己的应用程序,但仍然)。

第一种方法是使用两个停止暂停,前者将,后面

  / **
 *使用播放服务释放资源。这包括前台服务
 *状态和通知,唤醒锁,并可能在MediaPlayer。
 *
 *参数releaseMediaPlayer指示是否媒体播放器也应该释放或不
 * /
无效relaxResources(布尔releaseMediaPlayer){
    stopForeground(真正的);
    stopMonitoringPlaybackProgress();
    //停止并释放媒体播放器,如果可用
    如果(releaseMediaPlayer&安培;&安培;!MPLAYER = NULL){
        mPlayer.reset();
        mPlayer.release();
        MPLAYER = NULL;
    }
    //我们也可以释放无线锁,如果我们抱着它
    如果(mWifiLock.isHeld()){
        mWifiLock.release();
    }
}
 

这是一部分的 processPauseRequest()

 如果(mState == State.Playing){
        //暂停媒体播放器,并取消了前台服务状态。
        mState = State.Paused;
        mPlayer.pause();
        dispatchBroadcastEvent(ServiceConstants.EVENT_AUDIO_PAUSE); //通知广播接收机
        relaxResources(假); //暂停时,我们始终保留MP和通知
 

这是一部分的 processSto prequest()(简体):

 无效processSto prequest(布尔力,最终布尔stopSelf){
    如果(mState == State.Playing || mState == State.Paused ||力){
        mState = State.Stopped;
        //放开所有资源的...
        relaxResources(真正的);
        currentTrackNotification = NULL;
        giveUpAudioFocus();

    }
}
 

现在最核心的部分是下/跳过......

这是我做什么......

 无效processNextRequest(最终布尔isSkipping){
    processSto prequest(真,假); //这很重要,我们发布MP这里
    mState = State.Retrieving;
    dispatchBroadcastEvent(ServiceConstants.EVENT_TRACK_INFO_LOAD_START);
    //剪断但在这里你找回你的下轨道,当它准备...
    //你只是processPlayRequest()和从头开始
 

这是怎么样的MediaPlayer做它(在样本文件夹中),我还没有与它的问题。

话虽这么说,我知道,当你说你整个事情受阻,我已经看到了它和它的MP buggyness你的意思。如果你得到一个ANR我倒要看看日志吧。

有关的记录在这里就是我开始播放(大量的定制code已被省略了,但你能看到MP的东西):

  / **
 *开始播放下一首歌曲。
 * /
无效beginPlaying(轨道跟踪){
    mState = State.Stopped;
    relaxResources(假); //释放以外的所有的MediaPlayer
    尝试 {
        如果(跟踪!= NULL){
            createMediaPlayerIfNeeded();
            mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mPlayer.setDataSource(track.audioUrl);
        } 其他 {
            processSto prequest(真,假); //停止一切!
            返回;
        }
        mState =状态preparing。
        setUpAsForeground(); //服务

        / *删除所有code从REMOTECONTROLCLIENT,因为它会增加:)噪声* / A LOT

        //开始preparing媒体播放器在后台。当它这样做,它会调用
        //我们在preparedListener(这个类也就是在prepared()方法,因为我们设置
        //听者这个)。
        //直到媒体播放器prepared,我们*不能*调用start()就可以了!
        。MPLAYER prepareAsync();
        //我们是流从互联网上,我们要举行一个无线锁,其中prevents
        //从去时正在播放的歌曲睡觉WiFi无线电。
        如果(!mWifiLock.isHeld()){
            mWifiLock.acquire();
        }

    }赶上(IOException异常前){
        Log.e(MusicService,IOException异常播放下一首歌曲:+ ex.getMessage());
        ex.printStackTrace();
    }
}
 

最后一点,我已经注意到了媒体播放器阻止一切发生在音频流或源是不可用或不可靠的。

祝你好运!让我知道如果有什么事,具体的你想看到的。

I have a problem with the Android MediaPlayer when changing the dataSource of the player. According the specification of the MediaPlayer (http://developer.android.com/reference/android/media/MediaPlayer.html) I have to reset the player when changing the dataSource. This works fine, but as soon as the channelChanged method is called twice in quick succession the MediaPlayer.reset freezes the UI. I profile the code as seen here:

public void channelChanged(String streamingUrl)
{
    long m1 = System.currentTimeMillis();
    mMediaPlayer.reset();
    long m2 = System.currentTimeMillis();
    try
    {
        mMediaPlayer.setDataSource(streamingUrl);
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
    long m3 = System.currentTimeMillis();
    mMediaPlayer.prepareAsync();
    long m4 = System.currentTimeMillis();
    Log.d("MEDIAPLAYER", "reset: " + (m2 - m1));
    Log.d("MEDIAPLAYER", "setDataSource: " + (m3 - m2));
    Log.d("MEDIAPLAYER", "preparing: " + (m4 - m3));
}

reset: 3

setDataSource: 1

preparing: 0

reset: 3119

setDataSource: 2

preparing: 1

So apparently the reset is blocked by the asynchronous preparing of the first call (when I wait until the first stream starts and then call channelChanged() again, everything is fine).

Any ideas how to solve the problems? Should I execute the whole method in a separate thread? Basically I want to avoid that, because it seems not to be a good coding style and can possibly cause some further issues, e.g. when the user tries to start the player again, but the player is still in the reset method, which on the other hand seems to wait for the asyncPrepare method. It is not clear how the player would behave...

Is there any other good solution?

解决方案

MediaPlayer is a tricky bastard. I recommend you take a look at the sample app where the MediaPlayer bad design is made evident by looking at the mess of code you have to write around it to have a consistent media playback experience.

If anything, after looking at the sample, you see that when they want to skip a track, they essentially reset and release…

    mPlayer.reset();
    mPlayer.release();

…and later when they are ready to load a new track…

    try {
          mPlayer.reset();
          mPlayer.setDataSource(someUrl);
          mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
             @Override
              public void onPrepared(MediaPlayer mediaPlayer) {
                   //bam!
              }
          });
          mPlayer.prepareAsync();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    }

I have added the try/catch because on some devices/OS versions, the MediaPlayer is worse than others and sometimes it just does weird stuff. You should have an Interface/Listener that is capable of reacting to these situations

UPDATE:

This is a method I use when I stop (or pause) my Music Playback (mostly taken from the sample app, this is running in a service and it has been modified to suit my own app but still).

The first method is used by both stop and pause, the former passes true, the later false

/**
 * Releases resources used by the service for playback. This includes the "foreground service"
 * status and notification, the wake locks and possibly the MediaPlayer.
 *
 * @param releaseMediaPlayer Indicates whether the Media Player should also be released or not
 */
void relaxResources(boolean releaseMediaPlayer) {
    stopForeground(true);
    stopMonitoringPlaybackProgress();
    // stop and release the Media Player, if it's available
    if (releaseMediaPlayer && mPlayer != null) {
        mPlayer.reset();
        mPlayer.release();
        mPlayer = null;
    }
    // we can also release the Wifi lock, if we're holding it
    if (mWifiLock.isHeld()) {
        mWifiLock.release();
    }
}

This is part of the processPauseRequest():

if (mState == State.Playing) {
        // Pause media player and cancel the 'foreground service' state.
        mState = State.Paused;
        mPlayer.pause();
        dispatchBroadcastEvent(ServiceConstants.EVENT_AUDIO_PAUSE);//notify broadcast receivers
        relaxResources(false); // while paused, we always retain the mp and notification

And this is part of the processStopRequest() (simplified):

void processStopRequest(boolean force, final boolean stopSelf) {
    if (mState == State.Playing || mState == State.Paused || force) {
        mState = State.Stopped;
        // let go of all resources...
        relaxResources(true);
        currentTrackNotification = null;
        giveUpAudioFocus();         

    }
}

Now the core part is the next/skip…

This is what I do…

void processNextRequest(final boolean isSkipping) {
    processStopRequest(true, false); // THIS IS IMPORTANT, WE RELEASE THE MP HERE
    mState = State.Retrieving;
    dispatchBroadcastEvent(ServiceConstants.EVENT_TRACK_INFO_LOAD_START);
    // snipped but here you retrieve your next track and when it's ready…
    // you just processPlayRequest() and "start from scratch"

This is how the MediaPlayer sample does it (found in the samples folder) and I haven't had problems with it.

That being said, i know what you mean when you say you get the whole thing blocked, I've seen it and it's the MP buggyness. If you get an ANR I'd like to see the log for it.

For the record here's how I "begin playing" (a lot of custom code has been omited but you get to see the MP stuff):"

/**
 * Starts playing the next song.
 */
void beginPlaying(Track track) {
    mState = State.Stopped;
    relaxResources(false); // release everything except MediaPlayer
    try {
        if (track != null) {
            createMediaPlayerIfNeeded();
            mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mPlayer.setDataSource(track.audioUrl);
        } else {
            processStopRequest(true, false); // stop everything! 
            return;
        }
        mState = State.Preparing;
        setUpAsForeground(); //service

        /* STRIPPED ALL CODE FROM REMOTECONTROLCLIENT, AS IT ADDS A LOT OF NOISE :) */

        // starts preparing the media player in the background. When it's done, it will call
        // our OnPreparedListener (that is, the onPrepared() method on this class, since we set
        // the listener to 'this').
        // Until the media player is prepared, we *cannot* call start() on it!
        mPlayer.prepareAsync();
        // We are streaming from the internet, we want to hold a Wifi lock, which prevents
        // the Wifi radio from going to sleep while the song is playing.
        if (!mWifiLock.isHeld()) {
            mWifiLock.acquire();
        }

    } catch (IOException ex) {
        Log.e("MusicService", "IOException playing next song: " + ex.getMessage());
        ex.printStackTrace();
    }
}

As a final note, I've noticed that the "media player blocking everything" happens when the audio stream or source is unavailable or unreliable.

Good luck! Let me know if there's anything specific you'd like to see.

这篇关于Android的MediaPlayer的复位冻结UI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆