I am very new to android just started with Android currently referring the below code from past 2 days, not able to fix the issue.
Code:
https://github.com/quocnguyenvan/media-player-demo
Issue: Let's say we 4 Songs in the ListView when we click on the first song play it for some time and pause it without clicking on stop.
As soon as we click on the second song the first song starts playing we cannot play the second song unless we click on stop of the first song How to solve this issue.
The issue with code not able to figure out and fix it.I have referred many posts before posting on StackOverflow but could not make it, any suggestion or guidance is highly appreciated.
Problematic code:
// play music
viewHolder.ivPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(flag){
mediaPlayer = MediaPlayer.create(context, music.getSong());
flag = false;
}
if(mediaPlayer.isPlaying()) {
mediaPlayer.pause();
viewHolder.ivPlay.setImageResource(R.drawable.ic_play);
} else {
mediaPlayer.start();
viewHolder.ivPlay.setImageResource(R.drawable.ic_pause);
}
}
});
// stop
viewHolder.ivStop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!flag) {
mediaPlayer.stop();
mediaPlayer.release();
flag = true;
}
viewHolder.ivPlay.setImageResource(R.drawable.ic_play);
}
});
These are the steps I used for playing song you can try to sync with my steps to resolve the error.
private void initMembers() {
//initialize the members here
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
//media prepare
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mediaPlayer) {
//media player prepared
togglePlayPausePlayer();
}
});
//media completion
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(final MediaPlayer mediaPlayer) {
//handle the media play completion
handleOnCompletionLogic();
}
});
}
after that whenever I try to play the song. I call this method.
public void playMusic(final MusicModel musicModel) {
//here play the music with data in below
try {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
mMediaPlayer.reset();
mMediaPlayer.setDataSource(musicModel.getUrl());
mMediaPlayer.prepareAsync();
} catch (IOException | IllegalStateException exp) {
exp.printStackTrace();
showMessage(getString(R.string.error_unable_play));
}
}
above resets the playing song and prepare the player for another song.
and OnPreparedListener it calls the togglePlayPausePlayer() which play and pause the song accordingly.
private void togglePlayPausePlayer() {
//here handle the logic for play and pause the player
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
setPlayAndPause(false);
} else {
mMediaPlayer.start();
setPlayAndPause(true);
}
}
The key is we initialize the player and set an onPrepareListener for the media player to get prepared and then play the song, which will check if it's playing then it will stop and else it will play.
hope this may help you.
For on click on ListView you need to reset the media player, change the data source and start playing with new song.
mp.reset();
mp.setDataSource("song you selected from ListView");
mp.prepare();
mp.start();
Related
enter code here
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
btPause.setVisibility(View.GONE);
btPlay.setVisibility(View.VISIBLE);
mediaPlayer.seekTo(0);
}
});
}
#SuppressLint("DefaultLocale")
private String convertFormat(int duration) {
return String.format("%02d:%02d"
, TimeUnit.MILLISECONDS.toMinutes(duration)
,TimeUnit.MILLISECONDS.toSeconds(duration) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration)));
}
}
enter code here
I think you are creating a Mediaplayer instance everytime. Make sure that instead of the instantiation is
MediaPlayer mp;// above the onCreate section
mp=MediaPlayer.create(this, R.raw.animal_kit);
instead of
MediaPlayer mp=MediaPlayer.create(this, R.raw.animal_kit);
I am making a chat application and I have implemented the feature for sending audio messages.But here I find one thing which I don't want it to happen.It is that whenever my adapter gets updated,The media player starts loading again. In this way there will be an issue for if someone is listening to an audio and the user at other end sends a message ,the media player stops and it loads again.Here is the code of my adapter.
final MediaPlayer mediaPlayer;
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
handler = new Handler();
try {
mediaPlayer.setOnCompletionListener(mediaPlayer1 -> {
mediaPlayer1.stop();
binding.audioSeekbar.setProgress(0);
});
if (mediaPlayer.isPlaying()){
mediaPlayer.stop();
mediaPlayer.release();
}
mediaPlayer.setDataSource(finalUrlToLoad[1]);
mediaPlayer.setVolume(1f, 1f);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(mediaPlayer1 -> {
int totalDuration = mediaPlayer1.getDuration();
binding.totalDurationAudio.setText(createTimeLabel(totalDuration));
binding.loadingAudio.setVisibility(GONE);
binding.playPauseAudio.setVisibility(VISIBLE);
});
} catch (IOException e) {e.printStackTrace();}
binding.playPauseAudio.setOnClickListener(view -> {
if (mediaPlayer.isPlaying()){
handler.removeCallbacks(runnable);
mediaPlayer.pause();
binding.playPauseAudio.setImageResource(R.drawable.pause_to_play);
Drawable drawable = binding.playPauseAudio.getDrawable();
if( drawable instanceof AnimatedVectorDrawable) {
AnimatedVectorDrawable animation = (AnimatedVectorDrawable) drawable;
animation.start();
}
}else {
mediaPlayer.seekTo(binding.audioSeekbar.getProgress());
mediaPlayer.start();
handler.post(runnable);
binding.playPauseAudio.setImageResource(R.drawable.play_to_pause);
Drawable drawable = binding.playPauseAudio.getDrawable();
if( drawable instanceof AnimatedVectorDrawable) {
AnimatedVectorDrawable animation = (AnimatedVectorDrawable) drawable;
animation.start();
}
}
});
runnable = () -> {
int totalTime = mediaPlayer.getDuration();
binding.audioSeekbar.setMax(totalTime);
int currentPosition = mediaPlayer.getCurrentPosition();
binding.audioSeekbar.setProgress(currentPosition);
binding.totalDurationAudio.setText(createTimeLabel(totalTime));
Log.d("time", String.valueOf(currentPosition));
handler.postDelayed(runnable,1000);
};
binding.audioSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (b){
mediaPlayer.seekTo(i);
seekBar.setProgress(i);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
mediaPlayer.setOnBufferingUpdateListener((mediaPlayer1, i) -> binding.audioSeekbar.setSecondaryProgress(i));
Here finalurltoload[1] is the url for the audio.
Now what do I need to do in order to prevent loading it again and again.
I will be really grateful to who answer this question.
Thanks😊.
It's hard to tell from this code but I assume this is all set in your onBind event? If so, then this means every time RecyclerView creates a new holder and binds it, the associated media will be prepped and loaded, and whichever is the 'last holder to have been called with onBind, "wins" (and is what MediaPlayer will be loaded with). Since by default RecyclerView typically creates multiple holders up front, you are seeing your MediaPlayer being "loaded" multiple times.
You probably just don't want to do the initialization of each audio message in the onBind. Instead, just use the onBind event to initialize state variables (duration, progress, etc.) to some default value, hide them and bind the specific audio Uri. Then when the user takes some action like tapping on the holder, you unhide an indeterminate progress bar while the initialization takes place, and in the onPrepared() event unhide the state information (duration, progress, seekbar, etc.), and finally hide the indeterminate progress bar and start the audio.
I assume you are also sending over the sound file as part of your messaging app (i.e. not storing it on the web somewhere in a central location?), and this file gets stored in an app-specific storage location? If so, you don't need to worry about persisting the permission to that URI, but if that isn't the case you will.
First extract the media player code into singleton class like AudioManager.
Add few method like setMediaUpdateListener that set a callback for seek duration. and togglePlayPause to play or pause the audio.
Passed the message id or any unique identifier to the audio manager while playing the video.
In Adapter class onBind Method.
First Compare the id and playing Id is same like AudioManager.getInstance().isPlaying(messageId);
If yes then set the seekUpdatelistner to the audio manager class.
also update the play/pause icon based on AudioManager.isPlaying() method.
3.if user play other message by clicking play button. call AudioManager.play(message) method.In which we release the previous message and play the new one.
If current message is not playing then reset the view on non-playing state.
If Auto play is enabled then you need to check if audioManager is free then only you can play the last message otherwise ignored.
Like a class who are managing the audio for you and store all the state.
class AudioManager {
public static AudioManager instance;
final MediaPlayer mediaPlayer;
private AudioListener audioListener;
private Uri currentPlaying;
public AudioManager getInstance() {
if (instance == null) {
instance = new AudioManager();
}
}
public void play(Uri dataUri) {
if (mediaPlayer != null && currentPlaying == null || currentPlaying.equals(dataUri)) {
if (!mediaPlayer.isPlaying) {
mediaPlayer.play();
}
return;
} else if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
handler = new Handler();
try {
mediaPlayer.setOnCompletionListener(mediaPlayer1 -> {
mediaPlayer1.stop();
sendProgress(0);
});
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
mediaPlayer.release();
}
mediaPlayer.setDataSource(dataUri;
mediaPlayer.setVolume(1f, 1f);
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(mediaPlayer1 -> {
int totalDuration = mediaPlayer1.getDuration();
sendTotalDuration(totalDuration);
});
} catch (IOException e) {
e.printStackTrace();
}
}
public void pause() {
// update the pause code.
}
public void sendProgress(int progress) {
if (audioListener != null) {
audioListener.onProgress(progress);
}
}
public void sendTotalDuration(int duration) {
if (audioListener != null) {
audioListener.onTotalDuraration(duration);
}
}
public void AudioListener(AudioListener audioListener) {
this.audioListener = audioListener;
}
public interface AudioListener {
void onProgress(int progress);
void onTotalDuraration(int duration);
void onAudioPlayed();
void onAudioPaused():
}
}
When I click on any song from playlist first time it plays next song, this problem has only happened for the first time on second or more click on any song from the playlist it working fine.
But the major problem has, it unable to play next song after the end of any song anytime.
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.reset();
// play next song
if(listView_click_arg2 < (songPath.length - 1)){
listView_click_arg2=listView_click_arg2+1;
}
else{
// play first song
listView_click_arg2=0;
}
try {
playSong(songPath[listView_click_arg2]);
} catch (IOException e) {
e.printStackTrace();
}
}
});
private void playSong(String path) throws IllegalArgumentException, IllegalStateException, IOException {
startTime=0;
finalTime=0;
oneTimeOnly=0;
mediaPlayer.stop();
mediaPlayer=null;
mediaPlayer=new MediaPlayer();
mediaPlayer.setDataSource(path);
mediaPlayer.prepare();
mediaPlayer.start();
}
I found you have a problem because of mediaPlayer.reset() function. It says
When a MediaPlayer object is just created using new or after reset() is called, it is in the Idle state; and after release() is called, it is in the End state. Between these two states is the life cycle of the MediaPlayer object.
Please go through this link. There is a nice Flow chart:
https://developer.android.com/reference/android/media/MediaPlayer.html
I belive, after you remove mediaPlayer.reset() inside the onCompletionListener body, it will get solved.
Try adding release(). It looks like a out of memory issue.
private void playSong(String path) throws IllegalArgumentException, IllegalStateException, IOException {
startTime=0;
finalTime=0;
oneTimeOnly=0;
// changing the state of mediaPlayer inside its own callback is a bad practice.
//mediaPlayer.stop();
mediaPlayer.release(); // <<------ Add this before reference is gone.
mediaPlayer=null;
mediaPlayer=new MediaPlayer();
mediaPlayer.setDataSource(path);
mediaPlayer.prepare();
mediaPlayer.start();
}
I am new one in android development. I have a mp3 audio player code which play mp3 from url. I want to show loading dialog box when media player is buffering on prepare
Here is my Code I show a dialog on prepare but it continuous run and cannot play mp3.
I have no idea where i define smp.setOnPreparedListener. Please tell me that can i define this in play function or outside play function. Please Help Me here is my code. Thanks in Advance please
//Play MP3 Function
public void playSong(int naatindex){
// Play song
try {
mp.reset();
mp.setDataSource(naatpaths[naatindex]);
tv = (TextView) this.findViewById(R.id.mywidget);
tv.setSelected(true); // Set focus to the textview
tv.setText(naattitles[naatindex]);
mp.prepare();
mp.start();
// Changing Button Image to pause image
btnPlay.setImageResource(R.drawable.btn_pause);
// set Progress bar values
songProgressBar.setProgress(0);
songProgressBar.setMax(100);
// Updating progress bar
updateProgressBar();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
and here is my mp.setOnPreparedListener code
ProgressDialog progressDialog = ProgressDialog.show(this,
"Loading Title", "Loading Message");
mp.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
if (progressDialog != null && progressDialog.isShowing()){
progressDialog.dismiss();
}
mp.start();
}
});
In playSong(), you call :
mp.prepare();
mp.start();
If you directly start the player it would crash because it may be not ready to play. Try this:
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(naatpaths[naatindex]);
mp.setOnPreparedListener(new OnPreparedListener(){
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
if (progressDialog != null && progressDialog.isShowing())
progressDialog.dismiss();
}
});
mp.prepareAsync(); //this will prepare file a.k.a buffering
songProgressBar.setProgress(0);
songProgressBar.setMax(100);
// Updating progress bar
updateProgressBar();
If it still fails to play, check the stream url. It might be dead. Also try logging the state of the player. Check the stacktrace for any exceptions too.
I tried most of options avialable on stackoverflow, could you please help me!
I have a button which plays MediaPlayer instance when clicked, but the problem if I click double-click or clicked during playing media it will play two times even i set the button.setEnable(false) and button.setClickable(false), below is the code in Main.java, and I have set in xml android:onClick="playMedia"
MediaPlayer playMedia;
private void playGeneric(MediaPlayer mp, int name, Button button) {
button.setEnabled(false);
button.setClickable(false);
mp = MediaPlayer.create(this, name);
mp.start();
while (mp.isPlaying()) {
}
mp.stop();
mp.release();
mp = null;
button.setEnabled(false);
button.setClickable(false);
}
// play the Media
public void playMedia(View button) {
playGeneric(Media, R.raw.Media, (Button) findViewById(R.id.button1));
}
thanks a lot
1- You should have a single (preferably static) MediaPlayer instance per your activity/service
2- This loop while (mp.isPlaying()) is extremely bad practice, you just need to listen to the appropriate event
3- Before creating a new MediaPlayer instance you have to check that if the current one is not null then stop and release it
To solve your code you can do the following:
1- Let your class implements OnCompletionListener and override the method onCompletion(MediaPlayer mp) to be notified when the MediaPlayer finished and clean the resources (but you need to clean it as well when your activity/service get destroyed)
2-
static MediaPlayer playMedia;
private void playGeneric(MediaPlayer mp, int name, Button button) {
button.setEnabled(false);
button.setClickable(false);
if(mp !=null){
mp.stop();
mp.release();
}
mp = MediaPlayer.create(this, name);
mp.start();
}
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
playMedia = null;
button.setEnabled(true);
button.setClickable(true);
}