MediaPlayer skips sound when updating search string inside adapter call in fragment

I have a MediaPlayer that is contained in a custom ViewHolder and created by a RecyclerViewAdapter that is executed by the fragment. I try to update the search bar every second to show the progress of the sound that MediaPlayer is playing using the answer to this question :

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); } }); 

However, since I run it in the adapter from a fragment and not directly inside the action, I cannot use this line:

 MainActivity.this.runOnUiThread(new Runnable() { 

So instead, I passed getActivity() from the fragment to the RecyclerViewAdapter using the adapter constructor and set it as the parentActivity global variable.
Then I created the code to update the search bar in the RecyclerViewAdapter onBindViewHolder () as follows:

 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); } }); 

My problem is that now when I play audio, although the search engine is updating correctly, the sound temporarily pauses or β€œskips” every second. What could be causing this and how can I fix it?

EDIT: Each ViewHolder has its own SeekBar, and mMediaPlayer is defined as a global variable in the adapter.

+5
source share
2 answers

I would suggest that skipping and stopping the operation is caused by using too much, and more importantly, by having MULTIPLE instances of your activity. This is exactly what will happen if you copy it to each ViewHolder of your Recycler.

This is actually a very bad template and should be avoided by accident.

It is difficult to give you a solution because you did not specify how your application looks exactly (for example, these search characters are present in every ViewHolder, etc.). But, considering this to be the β€œworst” case, I suggest you pass another adapter listener that will listen to your seekBar or ViewHolder (depending on you, this interface may be more for advanced control). And the Fragment will do an updated thing within itself.

 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); } } } 

EDIT:

As you said, your solution has only a list of songs to play, and each element has its own SeekBar. In this case, I suggest you create a delegate class that will handle the music playback logic, and use ViewHolder only as a view (in other worlds, create a class for the Presenter level of your MVP). Below I posted a code that shows this idea more or less.

 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) } } 
+1
source

You may need to create mMediaPlayer in the fragment, and then pass the listener as a constructor parameter. As soon as your listener is called, you can pass the parameters of the media player to the listener and perform the remaining mMediaPlayer operations inside your fragment. That way you also have access to getActivity() context

You can pass this listener to your adapter from a fragment

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

Then call this listener from your adapter and pass your params to the OnMediaPlayerInvoked listener OnMediaPlayerInvoked that the rest of the operations are performed in the fragment

+1
source

Source: https://habr.com/ru/post/1275795/


All Articles