How to properly change current track for an Android MediaPlayer instance? - android

I'm using an Android MediaPlayer instance which is simple initialized by:
mediaPlayer = MediaPlayer.create(this,R.raw.conrnfield);
I also got this Thread updating a SeekBar according to the track playing progress.
updateTime = new Runnable() {
#Override
public void run() {
int currentPosition = 0;
int total = mediaPlayer.getDuration();
seekBar.setMax(total);
while (mediaPlayer != null && currentPosition < total) {
try {
Thread.sleep(500);
currentPosition = mediaPlayer.getCurrentPosition();
}
catch (InterruptedException ex) {return;}
catch (Exception e) {return;}
seekBar.setProgress(currentPosition);
}
}
};
I got a method to change the current track:
public void changeSong(View view) {
mediaPlayer.reset();
mediaPlayer.start();
mediaPlayer.selectTrack(2);
}
However, when I invoke this method, it causes the app to crash. I can see first at the log a
java.lang.IllegalStateException: Could not execute method of the activity
error message at the stacktrace.
I can't even know if my approach is wrong. This is whole log

You should use MediaPlayer.setDataSource after a call to MediaPlayer.reset. Note that the MediaPlayer.create methods are just helper methods that call MediaPlayer.setDataSource, and are suppose to be used to play one off media. In the example following context is your Activity so you could probably replace them with MainActivity.this.
Uri uri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(context.getPackageName())
.appendPath(String.valueOf(R.raw.conrnfield))
.build();
mediaPlayer.setDataSource(context, uri);

Related

MediaPlayer: start called in state 0, mPlayer(0x0)

I'm developing a simple music player, but an error appeared sometimes(just one line error):
Error: start called in state 0, mPlayer(0x0)
I have a MusicService for operating something about music. Part of it:
#Override
public void onCreate() {
super.onCreate();
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnCompletionListener(this);
}
...
public void play(String url) {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(url);
mediaPlayer.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
}
}
...
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
addTimer();
status = MUSIC_STATUS_PLAYING;
}
When I open a Activity, and bind MusicService, execute play(), the error appeared sometimes.
Any idea?
I had the same issue in Kotlin, and I was able to tackle it.
In my case I added the MediaPlayer.create() inside a Boolean statement, so let's say in fragment 1 I use a Boolean called "globalAudio", I set this Boolean globally true from "MyModel" class, hence it will execute MediaPlayer.create as normal since it is true outside the conditional body. But, if I move to fragment 2 and set the Boolean "globalAudio" to false and when I go back to fragment 1, the Logcat will throw me error "pause called in state 64, mPlayer(0xca47c070)" and "error (-38, 0)", because I am attempting to start the audio without Media.create() being set up.
I attached the problem and the solution below to make the understanding easier for you
Problem
if (MyModel.StaticData.globalAudio) { // A
homeAudio = MediaPlayer.create(requireActivity(), R.raw.background1)
homeAudio.setVolume(MyModel.StaticData.backgroundVolume, MyModel.StaticData.backgroundVolume)
}
if (homeAudio.isPlaying) { // B
homeAudio.pause() // pause
homeAudio.seekTo(0) // set start from 00:00
}
if (!homeAudio.isPlaying) { // C
homeAudio.start() // throws error if A is not executed
}
Solution
//A
homeAudio = MediaPlayer.create(requireActivity(), R.raw.background1)
homeAudio.setVolume(MyModel.StaticData.backgroundVolume, MyModel.StaticData.backgroundVolume)
if (homeAudio.isPlaying) { // B
homeAudio.pause() // pause
homeAudio.seekTo(0) // set start from 00:00
}
if (!homeAudio.isPlaying) { // C
homeAudio.start() // won't throw error because A is always executed
}
Here I provide you a Kotlin code that it can be automatically converted to Java in Android Studio:
var myAudio = MediaPlayer.create(requireActivity(), R.raw.[yourAudio])
myAudio.setVolume([leftChannelInFLoat], [rightChannelInFLoat])
if (myAudio.isPlaying) {
myAudio.pause()
myAudio.seekTo(0)
}
if (!hmyAudio.isPlaying) {
myAudio.start()
}

mediaplayer.getDuration() is throwing exception of illegalStateException in android 8.1, but working fine in lower versions

I'm using Media Player in my android application, which plays audio files from given url. I also want to update progress bar while playing the audio file. For that I'm using seekbar.setMax(mediaplayer.getDuration()) in onPrepated() method, but mediaplayer.getDuration() is throwing exception of illegalStateException & showing mediaplayer state = 0 or 1 while same code is working for android 7 & lower versions
My code goes below :
Map<String, String> map = new HashMap<>();
map.put("X-CSRF-Token",preferences.getString("token",null));
map.put("Cookie",preferences.getString("sessid",null));
map.put("Content-Type","application/json");
mediaPlayer.setDataSource(context,Uri.parse(mediaUrl),map);
mediaPlayer.prepareAsync();
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
seekBarProgress.setMax(mediaPlayer.getDuration());
seekBarProgress.setProgress(0);
primarySeekBarProgressUpdater(mediaPlayer.getCurrentPosition());
}
// method for updating seekbar
public void primarySeekBarProgressUpdater(final int i) {
try {
if (mediaPlayer.isPlaying()) {
handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
if (mediaPlayer != null && seekBarProgress != null) {
if (mediaPlayer.getDuration() > 0) {
seekBarProgress.setProgress(mediaPlayer.getCurrentPosition());
}
}
if (handler != null) {
handler.postDelayed(this, 1000);
}
}
}, 1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
According to this answer, postDelayed may causes the mediaPlayer to be in invalid state, so calling mediaPlayer.getDuration() will cause IllegalStateException.
For example:
if you press back so your media player released and your handler is still running and call mediaPlayer.getDuration()
Another Assumption is mediaPlayer.isPlaying(), according to open source:
/**
* Checks whether the MediaPlayer is playing.
*
* #return true if currently playing, false otherwise
* #throws IllegalStateException if the internal player engine has not been
* initialized or has been released.
*/
public native boolean isPlaying();

AndroidMediaplayer.create freeze (still running but dont do anything) the App

I was using MediaPlayer to play sounds in my App but from Its start to freeze the App:
Thats the code where I call create method. I moved the code inside an AsyncTask class :
public class BackgroundSound extends AsyncTask<Integer,Void,Void> {
MediaPlayer mpB;
MediaPlayer mpG;
Context ctx;
BackgroundSound(Context appctx)
{
ctx = appctx;
}
#Override
protected Void doInBackground(Integer... Params) {
switch (Params[0]){
case 0:
mpB = MediaPlayer.create(ctx, R.raw.sonidoemocion5sec);
mpB.setLooping(true);
mpB.setVolume(0.75f, 0.75f);
mpB.start();
break;
case 1:
if (mpG != null)
{
mpG.release();
mpG = null;
}
mpG = MediaPlayer.create(ctx, R.raw.ganador);
mpG.setVolume(1, 1);
mpG.start();
break;
case 2:
if (mpG != null){
mpG.release();
mpG = null;
}
mpG = MediaPlayer.create(ctx, R.raw.perder);
mpG.setVolume(1, 1);
mpG.start();
break;
}
return null;
}
}
Debuging I find that the freeze (app still running but dont make anything) occurred in the new line inside the method create from MediaPlayer.java file::
public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,
AudioAttributes audioAttributes, int audioSessionId) {
try {
MediaPlayer mp = new MediaPlayer();
I don't understand what's going on. Any tips?
The warn & Error Log exits:
Instead of using MediaPlayer.create(), use mp.setDataSource() method so that you can call mp.prepareAsync() and then set listener to listen for its completion like this:
mp.setDataSource(this, audioUri);
mp.prepareAsync();
mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
Now for this audioUri object of Uri, you can set it to your audio files in the raw folder by having this line of code in your project:
audioUri = Uri.parse("android.resource://com.yourpackagename/" + R.raw.your_audiofile_name);
This method prepares the mp asynchronously and doesn't block the UI
There are two possible solutions for this issue, but it is not sure that it will work properly as I have not tested it.
Solution 1.
Replace below line:
audiojuego = MediaPlayer.create(this, R.raw.sonidoemocion);
with:
audiojuego = MediaPlayer.create(getApplicationContext(), R.raw.sonidoemocion);
//As you are calling from doInBackground() method, this will not work properly.
Solution 2.
Initialize media player by your own way instead of default.
Uri url=Uri.parse("android.resource://"+getPackageName()+"/raw/sonidoemocion");
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

What is the difference between mediaplayer.create() and mediaplayer.prepareAsync()?

I have used the following code:
mp = MediaPlayer.create(this, Uri.parse("file://"+filePath));
mp.start();
This works fine. Then I wanted to play music from a folder
mp.setDataSource(this, Uri.parse("file://"+filePath));
mp.prepareAsync();
mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
Are there any performance differences between the two method?
You can check MediaPlayer create() source code to see the difference:
public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,
AudioAttributes audioAttributes, int audioSessionId) {
try {
MediaPlayer mp = new MediaPlayer();
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
mp.setAudioAttributes(aa);
mp.setAudioSessionId(audioSessionId);
mp.setDataSource(context, uri);
if (holder != null) {
mp.setDisplay(holder);
}
mp.prepare();
return mp;
} catch (IOException ex) {
Log.d(TAG, "create failed:", ex);
// fall through
} catch (IllegalArgumentException ex) {
Log.d(TAG, "create failed:", ex);
// fall through
} catch (SecurityException ex) {
Log.d(TAG, "create failed:", ex);
// fall through
}
return null;
}
Basically create() call is synchronous (it internally calls prepare()) and prepareAsync() is asynchronous.
The first approach ties up whatever thread you are on, long enough for MediaPlayer to read in the metadata of the media and prepare some buffers. If this is the main application thread, it means that your UI will be frozen while this is going on.
Sure, create method inits object in main thread. So code lines below it should wait for create.
On the other hand, prepare asynchronous opens a new thread to init object then notify you to run next operations while main thread run other lines.
Edit: As #CommonWares mentioned in the comment, mp.create() is a convenient method of calling mp.setDataSoucer() + mp.prepare() at the same time

Service not able to run twice

I am making a music player with a service but when i click a song in the music list, the music starts with no errors, but if i go back to the list and click another i get Attempt to call getDuration without a valid mediaplayer error (-38, 0)
My onStart method in the service:
#Override
public void onStart(final Intent i, int startid) {
Log.d(TAG, "Start music");
re = 0;
songUrl = i.getData().toString();
streamMusic = new Thread() {
public void run() {
Looper.prepare();
try {
re = 1;
music.reset();
music.setDataSource(songUrl);
music.prepare();
this.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
}
};
streamMusic.start();
}
I start the service with startService(i); and I have set a music.setOnPreparedListener so nothing is runned to early.
How do I make it work?
Try calling reset() method before calling prepare again.
Also, if you are calling prepare() and not prepareAsync() I don't see why you need to use the prepared listener.

Categories

Resources