I am learning how to play audio using MediaPlayer from this tutorial, which suggests using release() instead of stop() to STOP the audio. His explanation makes sense to me (free up the system resource as soon as you don't need it) and from a user perspective it works as expected, but I still feel like a bit weird that what's the point of using stop()? (https://stackoverflow.com/a/20580149/3466808)
fun stopPlayer1() = mediaPlayer?.stop()
fun stopPlayer2() {
mediaPlayer?.release()
mediaPlayer = null
}
So, which approach is better? Release as soon as user stops the audio? Or release only when the screen is no longer visible (onStop() called)?
take a look at the diagram in DOCS
MediaPlayer after release() is not "usable" anymore, you can nullify it safely. after onStop you still can call e.g. prepareAsync() and start playing again using single instance
edit: to comment
if (mMediaPlayer != null) {
try {
mMediaPlayer.stop();
} catch (Exception ignored) {
}
try {
mMediaPlayer.reset();
} catch (Exception ignored) {
}
try {
mMediaPlayer.release();
} catch (Exception ignored) {
}
mMediaPlayer = null;
}
Related
I have a custom view (a button) and I want it to have a certain sound when pressed.
private void playSound() {
if (mediaPlayer == null)
initMediaPlayer();
try {
if (mediaPlayer.isPlaying())
mediaPlayer.stop();
mediaPlayer.prepare();
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
private void initMediaPlayer() {
try {
AssetFileDescriptor afd = getContext().getAssets().openFd("music/number_tap.m4a");
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
} catch (IOException e) {
e.printStackTrace();
}
}
I call the playSound method every time the button is clicked. It works well as long as there is only one button on screen. No problem at all.
It crashes if I have more buttons on the screen and I press the first button a couple of times (works) and then some other button 2 times (crashes on the second press of the second button).
Any idea what could be wrong? Here's the crash stacktrace.
FATAL EXCEPTION: main
Process: com.matejhacin.multiflow, PID: 20991
java.lang.IllegalStateException
at android.media.MediaPlayer._prepare(Native Method)
at android.media.MediaPlayer.prepare(MediaPlayer.java:1163)
at com.matejhacin.multiflow.views.KeyButtonView.playSound(KeyButtonView.java:126)
at com.matejhacin.multiflow.views.KeyButtonView.onTouch(KeyButtonView.java:77)
From what I can see, you have your own implementation of that button KeyButtonView.java, and in that class you have mediaPlayer as a member variable, so when you have two buttons, respectively you have two instances of KeyButtonView class and two instances of mediaPlayer. Why don't you use one instance of mediaPlayer, let's say you make it static, and make playSound() and initMediaPlayer() methods synchronized and static, this should be a quick and simple change, just to test if there is the issue. And something else, you use onTouch() to handle a click, why don't you try with OnClickListener? If you do not use onTouch properly you will end with bunch of calls on the UI thread, and each blocking at prepare(), this is not good. Once you fix this issue, you should consider rewrite it with prepareAsync() and setOnPreparedListener().
im building a app to stream live radio over wifi. The problem im having is how to i keep the app playing when the user minimizes the app?
private void initializeMediaPlayer() {
player = new MediaPlayer();
try {
player.setDataSource("url");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
player.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
public void onBufferingUpdate(MediaPlayer mp, int percent) {
// playSeekBar.setSecondaryProgress(percent);
Log.i("Buffering", "" + percent);
}
});
}
You need to look into Services. They will let you do things in the background when the app minimizes.
When your app minimizes, you make an Intent that describes to your Service what is to be streamed and from what time. The Service will then construct a MediaPlayer and continue streaming. Also, a good idea would be to have a Notification that will let the user end this Service without opening the app.
One more condition is when the user maximizes the app after the Service has been playing for some time. In this case, you need a persistence mechanism to keep track. Like SharedPreferences or SQLite.
Documentation: http://developer.android.com/guide/components/services.html
You have to implement your mediaplayer as a service as an Activity will be in onstop state when minimized.
public class MyMediaPlayerService extends Service implements MediaPlayer.OnPreparedListener,MediaPlayer.OnCompletionListener,AudioManager.OnAudioFocusChangeListener {
static MediaPlayer mPlayer = null;
public MyMediaPlayerService() {
}
public int onStartCommand(Intent intent, int flags, int startId) {
if (mPlayer != null && mPlayer.isPlaying()) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
mPlayer = new MediaPlayer();
}
mPlayer = new MediaPlayer();
try {
mPlayer.setDataSource(intent.getStringExtra("streamurl"));
mPlayer.setLooping(true);
} catch (IOException e) {
e.printStackTrace();
}
mPlayer.setOnPreparedListener(this);
// Request audio focus for playback
mPlayer.prepareAsync();
mPlayer.setLooping(false);
mPlayer.setOnCompletionListener(this);
notification();
return START_NOT_STICKY;
}
Live radio streaming, it's long running process irrespective of UI, so you need to implement complete media player logic in service and your service will be take all the charge for streaming. As per as ui is concerned whatever UI component you are using to represent the streamed data can communicate with service you will implement.
And regarding start and stop streaming, it will be depend on your application design.
I think this approach will be open for all expected application design.
Let me know if you need any help.
I am experiencing an odd issue with a video streaming application I am working on. The actual streaming of video/audio is working fine on all of my test devices. However, on seemingly any device 4.0+, when using an RTSP URL, prepare() returns instantly (this causes an issue providing proper feedback to the users while a video is loading and interferes with a few other systems I have in place).
Below is the block of code where I initialize and setup my MediaPlayer, but keep a few things in mind:
My initPlayer method is called from an AsyncTask.
The video does eventually play correctly, but prepare returning instantly creates a lack of feedback to the user during a video load.
No errors of any kind occur during the entire process
start() is called on the MediaPlayer via the onPrepared method in my OnPreparedListener, which obviously becomes an issue when prepare() returns before it is actually ready to be played.
HTTP streams seem to work fine, and on every test device below 4.0 the issue does not occur.
I have been trying to fix this for a ridiculous amount of time, and haven't been able to find anyone else who has ran into this problem. Any ideas would be greatly appreciated.
public void initPlayer() {
//We first need to make sure the MediaPlayer isn't null
if(mMediaPlayer==null){
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(mediaPlayerPreparedListener);
mMediaPlayer.setOnCompletionListener(mediaPlayerCompletionListener);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
//If a video/stream has been chosen while another is already playing
else if(mMediaPlayer.isPlaying()){
mMediaPlayer.reset();
}
//Video is not in full screen mode
second = false;
try {
mMediaPlayer.setDataSource(videoString);
holder = mPreview.getHolder();
mMediaPlayer.setDisplay(holder);
mMediaPlayer.prepare();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//onPreparedListener
private OnPreparedListener mediaPlayerPreparedListener = new OnPreparedListener(){
public void onPrepared(MediaPlayer mp) {
mp.start();
vidPb.setVisibility(View.INVISIBLE);
}
};
Use mp.prepareAsync() as it is better for streaming media. Using prepare() blocks until MediaPlayer is ready for playback or an IllegalStateException occurs. Also, in android 4 (ICS), blocking on any UI thread is even more strict and may cause an ANR (Activity not responding) dialog to appear.
One final thought, try to avoid using e.printStackTrace(); in android apps.
Instead, use the Log.e("TAG_STRING", e.getMessage(), e); to print errors to the android logging system that you can access from logcat.
All in all, it should looks something like this:
try {
mMediaPlayer.setDataSource(videoString);
holder = mPreview.getHolder();
mMediaPlayer.setDisplay(holder);
mMediaPlayer.prepareAsync();
} catch (IllegalArgumentException e) {
Log.e("TAG_STRING", e.getMessage(), e);
} catch (SecurityException e) {
Log.e("TAG_STRING", e.getMessage(), e);
} catch (IllegalStateException e) {
Log.e("TAG_STRING", e.getMessage(), e);
} catch (IOException e) {
Log.e("TAG_STRING", e.getMessage(), e);
}
I have created a list of songs on click on the song i am able to play the song using MedaiPlayer. While one song is playing if the user clicks another song then i am stopping the media player and starting the player again. But I am getting illegalstateexception in reset(). Here is the code where I am getting the exception. How to stop a player properly? also why am i getting this exception. How to avoid it?
public void stopPlayer() {
try {
if (player != null) {
// Log.e("Trying to Stop "," Player ");
player.stop();
player.release();
player.reset();// causes IllegalstateException
player = null;
}
} catch (Exception e) {
player = null;
playerStatus = false;
e.printStackTrace();
}
}
try this :
player.reset();
player.release();
and also have a look at media player state diagram.
If you want to play again ,then use player.reset(),
player.release() means that it releases the player object so you have to re-intialise the player. So first you use reset() and then release(). release() is used when your player object no longer working. When your activity destroys release() method to be used for good practice.
Whenever you want to stop it:
if(player!=null)
{
if(player.isPlaying())
player.stop();
player.reset();//It requires again setDataSource for player object.
}
Whenever your player no longer to be needed:
if(player!=null)
{
if(player.isPlaying())
player.stop();
player.reset();//It requires again setDataSource for player object.
player.release();
player=null; // fixed typo.
}
Though the accepted answer works, This is a better way to achieve the task
private void stopSong() {
if(mediaPlayer!=null) {
if(mediaPlayer.isPlaying()) {
mediaPlayer.reset();// It requires again setDataSource for player object.
mediaPlayer.stop();// Stop it
mediaPlayer.release();// Release it
mediaPlayer = null; // Initialize it to null so it can be used later
}
}
}
Are you planning on reusing the player again, or are you done with the player? If you're done with the player, call release() and not reset(). If you plan on reusing the player, call reset() and not release().
reset() resets the player to its uninitialized state.
release() frees all resources associated with the player.
The Media Player State Diagram shows, and also states:
Calling stop() stops playback and causes a MediaPlayer in the Started, Paused, Prepared or PlaybackCompleted state to enter the Stopped state.
Once in the Stopped state, playback cannot be started until prepare() or prepareAsync() are called to set the MediaPlayer object to the Prepared state again.
That means, that after calling stop(), we should call prepare() on the same audio file if we wish to play it again. Otherwise calling start() again won't do anything.
As prepare() might throw exception, we should wrap it in a try-catch block, like this:
public void stopAudio(View view) {
mplayer.stop();
try {
mplayer.prepare();
} catch (IOException e) {
Log.e("stopAudio", "Unable to prepare() mplayer after stop()", e);
}
}
I am using a MediaPlayer instance in order to stream audio files from an Internet location. The audio player is in a separate activity. The user can select from a list of audio files and come to this activity to play the audio.
Now the user might go back to the previous activity (with the list) and select another audio file. In this case, I want to stop any other audio that is playing and start playing the new audio which was selected.
Is there any way I can know whether an audio file is playing without having to hold on to the MediaPlayer object?
Thanks.
Edit
I did find out how to know whether an audio is playing.
We can do it by using an object of AudioManager and calling isAudioPlaying(). This will return a true if any audio is playing.
Now the other question, how do I stop an audio currently playing? I do not have an instance of the MediaPlayer object which was created to start the audio (coz the user has already left the activity once and has come back with a new object of the activity and thus a new instance of the MediaPlayer)
You'll need to call stop() on the MediaPlayer instance. To make this work in your application, you'll either need to:
Call stop() within the audio playing activity (in onDestroy()), for example
Create a Service to play audio, and communicate with it from both activities
Using a Service will allow your code to continue running outside of the Activity life-cycle, and is the only way to persist a MediaPlayer object like you need to in this case.
Alternatively, you may be able to create a custom subclass of Application and store the MediaPlayer there, but using a Service is considered better practice.
I found way to check whether audio stream (AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION and etc.) is currently busy using reflection:
/**
* Unhide android api: check is stream is active now (AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION...),
* uses reflection
* #param audioStream
* #return
*/
public static boolean isStreamActive(int audioStream) {
Class<?> audioSystemClazz = null;
Boolean res = false;
try {
audioSystemClazz = Class.forName("android.media.AudioSystem");
if (null != audioSystemClazz) {
// isStreamActive
Method method = audioSystemClazz.getDeclaredMethod("isStreamActive", new Class<?>[] { int.class,
int.class });
if (null != method) {
res = (Boolean) method.invoke(null, audioStream, 0);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return res;
}
Here's some handy code to sleep until audio is done playing:
AudioManager manager = (AudioManager)this.getSystemService(Context.AUDIO_SERVICE);
while(manager.isMusicActive())
{
Log.d("music", "music is active");
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
Log.e("music", "interrupted waiting for music to stop");
}
Log.d("music", "done playing music");
}
#lostintransit "Would it be better to use a service, a static variable or a singleton class? What would be the better design option?"
I think a service is what you want. The built-in media player and Pandora's app both use a service to ensure the music isn't tied to the Activity lifecycle.
If I'm understanding why you'd use a singleton or static I don't think it will accomplish what you want. The singleton/static will only be enforced within a single process in Linux. If you launch your Activity, then close it, then launch it again, they will run in different processes.
Try this...
if (mp1 != null && mp1.isPlaying()) {
mp1.stop();
}
Where mp1 is the MediaPlayer