从片段中的适配器调用更新搜索栏时,MediaPlayer 跳过音频

MediaPlayer skipping audio when updating seekbar from within Adapter call in Fragment

我有一个 MediaPlayer,它包含在自定义 ViewHolder 中,由 RecyclerViewAdapter 创建,而 RecyclerViewAdapter 是由 Fragment 运行 创建的。我正在尝试每秒更新搜索栏以显示 MediaPlayer 正在播放的音频的进度,使用 this question's answer:

private Handler mHandler = new Handler();
//Make sure you update Seekbar on UI thread
MainActivity.this.runOnUiThread(new Runnable() {

    @Override
    public void run() {
        if(mMediaPlayer != null){
            int mCurrentPosition = mMediaPlayer.getCurrentPosition() / 1000;
            mSeekBar.setProgress(mCurrentPosition);
        }
        mHandler.postDelayed(this, 1000);
    }
});

但是,由于我是 运行 从一个片段中将它连接到一个适配器中,而不是直接在 activity 中,我不能使用这一行:

MainActivity.this.runOnUiThread(new Runnable() {

因此,我使用适配器的构造函数将 getActivity() 从片段传递到 RecyclerViewAdapter 并将其设置为全局变量 parentActivity.
然后我创建了代码来更新 RecyclerViewAdapter 的 onBindViewHolder() 中的搜索栏,如下所示:

final Handler mHandler = new Handler();
this.parentActivity.runOnUiThread(new Runnable(){

     @Override
     public void run(){
          if (mMediaPlayer != null){
               int mCurrentPosition = mMediaPlayer.getCurrentPosition();
               myViewHolder.seekBar.setProgress(mCurrentPosition);
          }
          mHandler.postDelayed(this, 1000);
     }
});

我的问题是,现在当我播放音频时,虽然搜索栏正确更新,但音频会短暂暂停,或每秒 "skips"。是什么原因造成的,我该如何解决?

编辑:每个 ViewHolder 都有自己的 SeekBar,并且 mMediaPlayer 在适配器中定义为全局变量。

我猜想跳过和暂停的事情是由于过多地使用 main thred 造成的,更重要的是你的 activity 有多个实例。如果将它复制到 Recycler 的每个 ViewHolder,就会发生这种情况。

实际上这是一个非常糟糕的模式,应该避免。

很难为您提供解决方案,因为您没有明确说明您的应用程序究竟是什么样子(例如,每个 ViewHolder 中是否存在搜索栏等)。但假设它是 'worst' 情况,我建议您将另一个侦听器传递给适配器,它将侦听您的 seekBar 或 ViewHolder(取决于您,实际上这个接口可能更大以用于扩展控制)。 Fragment 会在其内部执行刷新操作。

class Adapter(... startPlayingListener: (SeekBar) -> Unit) {

   fun onBindViewHolder() {
      whateverButton.setOnClickListener {startPlayingListener.invoke(seekBar)}
   }
}


class Fragment(...) {

   fun bindAdapter() {
       adapter = YourAdapter(..., this::onPlayerStart)
   }

   fun onPlayerStart(seekBar: SeekBar) {
        this.parentActivity.runOnUiThread(
        {
            if (mMediaPlayer != null){
               int mCurrentPosition = mMediaPlayer.getCurrentPosition();
               seekBar.setProgress(mCurrentPosition);
            }
            mHandler.postDelayed(this, 1000);
        }
   }
}

编辑:

如您所说 - 您的解决方案只有一个要播放的歌曲列表,并且每个项目都有自己的 SeekBar。在这种情况下,我建议您创建一个委托 class 来处理播放音乐的逻辑,并仅将 ViewHolder 用作视图(在其他情况下 - 为您的 MVP 的 Presenter 层创建 class) .下面我发布了或多或少显示这个想法的代码。

public interface YourPlayerView() {
    void updateSeekBar(int currentPosition);
}

public interface YourPlayerPresenter {
    void startPlaying(YourPlayerView view, trackID or something)
    void stopPlaying(YourPlayerView view, trackID or something)
}

class YourPlayer(...) implements YourPlayerPresenter {
    private MediaPlayer;

   @Override
    public void startPlaying(YourPlayerView view, trackId or something) {
        //start the track - possibly pass it as an argument of this method
        //start your handler calling view.updateSeekBar(...)
    }
}

public class ViewHolder(YourPlayerPresenter player) implements YourPlayerView {

    void bind(...) {
        buttonStart.setOnClickListener(player.startPlaying(this, trackId);
    }

    @Override
    public void updateSeekBar(int currentPosition) {
        mSeekBar.setProgress(currentPositition)
    }
}

您可能需要在片段中创建 mMediaPlayer,然后将侦听器作为构造函数参数传递给适配器。调用侦听器后,您可以将媒体播放器参数传递给侦听器,并在片段内执行其余的 mMediaPlayer 操作。 通过这种方式,您也可以访问 getActivity() 上下文

您可以将此侦听器从片段传递给您的适配器

public interface OnMediaPlayerInvoked {
void onSeekBarPosition(your params);
}

然后从您的适配器调用此侦听器并将 your params 传递给侦听器 OnMediaPlayerInvoked,以便可以在片段

中执行其余操作