I start the mediaplayer to play a piece of music in a thread and I'm wondering why mediaplayer can continue working even the thread is already dead. Here is the example:
public class MusicThread extends Thread {
MediaPlayer mp;
public MusicThread(Context context) {
mp = MediaPlayer.create(context, R.raw.music);
}
#Override
public void run() {
mp.start();
Log.d("MusicThread", "mp started");
}
}
Then inside the activity:
MusicThread musicThread = new MusicThread(this);
musicThread.start();
Here is my confusion:
After musicThread.start(), the music begins. Also, the thread completes becasue we can see the log generated by Log.d(...) in LogCat and we can see the false returned by musicThread.isAlive().
I have the reference to musicThread such that it won't be GC immediately when it finishes.
But what about the mediaplayer? It continues working but the thread which it resides has died already. It seems that it's in a weird state in this case. Is it still working in musicThread? If yes, why and how? If not, where it is?
mp.start() is not a blocking call. So your thread won't wait until playing is finished. mp.start() call returns immediately. You don't even need a separate thread to call it.
PS : If you want to get a callback when the playing is finished just use setOnCompletionListener. Once the playing is completed, public void onCompletion(MediaPlayer mp) will be called
Related
I want my button to be "spammable". This means that if I tap on the button repeatedly the MediaPlayer starts all over again.
firstButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (firstTextView.getText().equals("Hello world!")) {
firstTextView.setText("You clicked!");
} else {
firstTextView.setText("Hello world!");
}
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.start();
}
});
When I interrupt the MediaPlayer when it is playing, it stops and never starts again. Why?
EDIT: The problem is that I called stop(). Thanks for pointing that out.
As per the documentation, you need to re-prepare the MediaPlayer (emphasis mine):
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.
Seems you are stopping the player because you are calling mediaPlayer.stop() this makes the MediaPlayer state to go in Stopped state. It will continue to play again when you call prepare() or prepareAsync() and has its preparation callback fired to start the playing media.
I am able to play an mp3 file using android's MediaPlayer object. But I would like to play between a range of milliseconds for example between 30000 ms to 40000 ms ( 10 seconds only ). How can I achieve this?
Currently the following code is what I have,
private MediaPlayer mPlayer;
public void play() {
try {
mPlayer = MediaPlayer.create(getApplicationContext(), R.raw.mp3_file);
if (mPlayer != null) {
int currentPosition = mPlayer.getCurrentPosition();
if (currentPosition + 30000 <= mPlayer.getDuration()) {
mPlayer.seekTo(currentPosition + 30000);
} else {
mPlayer.seekTo(mPlayer.getDuration());
}
mPlayer.start();
}
}
catch(Exception e) {
}
}
Any help is greatly appreciated. Thank you!
You can use the method:
public int getCurrentPosition ()
to obtain the current time in milSeconds maybe inside a Handler that runs every 1000 milSeconds and tests to see:
if(mPlayer.getCurrentPosition() >= (mPlayer.getDuration + 40000));
Dont forget to release the media file when you're done using it:
public void release();
mPlayer.release();
Releases resources associated with this MediaPlayer object. It is
considered good practice to call this method when you're done using
the MediaPlayer. In particular, whenever an Activity of an application
is paused (its onPause() method is called), or stopped (its onStop()
method is called), this method should be invoked to release the
MediaPlayer object, unless the application has a special need to keep
the object around. In addition to unnecessary resources (such as
memory and instances of codecs) being held, failure to call this
method immediately if a MediaPlayer object is no longer needed may
also lead to continuous battery consumption for mobile devices, and
playback failure for other applications if no multiple instances of
the same codec are supported on a device. Even if multiple instances
of the same codec are supported, some performance degradation may be
expected when unnecessary multiple instances are used at the same
time.
The best approach is to use a Handler to time the stopping of the playback. Start the player and then use the Handler's postDelayed to schedule the execution of a Runnable that will stop the player. You should also start the player only after the initial seek completes. Something like this:
public class PlayWord extends Activity implements MediaPlayer.OnSeekCompleteListener {
Handler mHandler;
MediaPlayer mPlayer;
int mStartTime = 6889;
int mEndTime = 7254;
final Runnable mStopAction = new Runnable() {
#Override
public void run() {
mPlayer.stop();
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final TextView tv = new TextView(this);
tv.setText("Playing...");
setContentView(tv);
mHandler = new Handler();
mPlayer = MediaPlayer.create(this, R.raw.nicholas);
mPlayer.setOnSeekCompleteListener(this);
mPlayer.seekTo(mStartTime);
}
#Override
public void onDestroy() {
mPlayer.release();
}
#Override
public void onSeekComplete (MediaPlayer mp) {
mPlayer.start();
mHandler.postDelayed(mStopAction, mEndTime - mStartTime);
}
}
Note also that the MediaPlayer.create method you are using returns a MediaPlayer that has already been prepared and prepare should not be called again like you are doing in your code.on the screen. I also added a call to release() when the activity exits.
Also, if you want to update the UI when the seek completes, be aware that this method is usually called from a non-UI thread. You will have to use the handler to post any UI-related actions.
I'm copied this from: Android: How to stop media (mp3) in playing when specific milliseconds come?
I'm trying to write a function to play a short sound (in /res/raw) in my program, called at effectively random times throughout the program. So far I have this function:
public void playSound() {
MediaPlayer mp = new MediaPlayer();
mp = MediaPlayer.create(this, R.raw.ShortBeep);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setLooping(false);
mp.start();
}
It works fine for awhile, but after exactly 30 plays of the sound, it stops making sound.
According to the Docs
... failure to call release() may cause subsequent instances of MediaPlayer objects to fallback to software implementations or fail altogether.
When you are done with it call mp.release() so that it can release the resources. I don't know what the limit is and I'm sure it depends on many factors. Either way you should be calling this function on your MediaPlayer object, especially if it will be used more than once.
I've just solved the exact same problem, but I'm using Xamarin. I ended up changing from holding on to a MediaPlayer instance for the lifetime of the activity to creating an instance each time I want to play a sound. I also implemented the IOnPreparedListener and IOnCompletionListener.
Hopefully you can get the idea despite it being C# code
public class ScanBarcodeView :
MvxActivity,
MediaPlayer.IOnPreparedListener,
MediaPlayer.IOnCompletionListener
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.ScanBarcodeView);
((ScanBarcodeViewModel) ViewModel).BarcodeScanFailed += (sender, args) => PlaySound(Resource.Raw.fail);
((ScanBarcodeViewModel) ViewModel).DuplicateScan += (sender, args) => PlaySound(Resource.Raw.tryagain);
}
private void PlaySound(int resource)
{
var mp = new MediaPlayer();
mp.SetDataSource(ApplicationContext, Android.Net.Uri.Parse($"android.resource://com.company.appname/{resource}"));
mp.SetOnPreparedListener(this);
mp.SetOnCompletionListener(this);
mp.PrepareAsync();
}
public void OnPrepared(MediaPlayer mp)
{
mp.Start();
}
public void OnCompletion(MediaPlayer mp)
{
mp.Release();
}
}
So, each time I want a sound to be played I create a MediaPlayer instance, so the data source, tell it that my Activity is the listener to Prepared and Completion events and prepare it. Since I'm using PrepareAsync I don't block the UI thread. When the media player is prepared the Start method on the MediaPlayer is called, and when the sound has finished playing the MediaPlayer object is released.
Before I made these changes I would get to 30 sounds played and it would all stop working. Now I've gone way past 30, also multiple sounds can be played simultaneously.
Hope that helps.
I am using MediaPlayer to play a looping audio. It needs to play within a service. After several seconds it terminates. I've checked to see that my service is still running and it is. When I moved the code to an activity it worked fine and never stops. This has led me to believe that the problem has to do with threading. In the activity, MediaPlayer is running on the UI thread but in my service it runs in a thread of its own and one that is not bound to the UI thread. Perhaps this has something to do with it. I also declared my MediaPlayer object as a private static field but that doesn't help either.
Logcat only indicates that the "Media server died" with no further reason why.
If MediaPlayer needs to operate within the UI thread, how would I access that from within a service or any thread spawned by the service?
I am having this problem on Android 4.1. Here is approximately what the code looks like:
class PlaySound implements Runnable
{
private static MediaPlayer mp;
public void run()
{
this.mp = MediaPlayer.create(this.context, R.raw.sound);
this.mp.setVolume(1, 1);
this.mp.setLooping(true);
this.mp.start();
do
{
// Do something until told to stop thread.
}
while (true);
}
}
I also moved the private field to my service and even removed the do...while so that the thread could terminate immediately after playing started but this didn't help either.
EDIT:
This appears to be a problem in Android 4.1. I tested my app on 2.3 on a device from the same manufacturer and it does not experience this problem. In fact, the 2.3 device had much less resources and a slower processor, so if anything it should have been terminated if resources were an issue.
The problem here is that i create a MediaPlayer inside a thread that is managed by the Service. Use Handler(getMainLooper()) to start and stop MediaPlayer.
final Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
mediaPlayer.start();
}
});
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}
, 30 * 1000);
I've a multi-threaded app where main thread initiate two threads:
MakeRequest Thread
QueueListener Thread
MakeRequest thread after each second query a printer on LAN to request some data and perform some calculations on it and feed it to a Queue on which the second thread is listening. As soon as the data is available in the Queue, QueueListener thread dequeue a record from the Queue and initiate another thread i.e. MediaPlayer thread this thread is responsible for playing 7 to 8 files depending on the string received. For which I am using the following code.
MediaPlayer mp = MediaPlayer.create(context, Uri.fromFile(new File(q[voiceIndex])));
mp.setOnCompletionListener(mCompletionListener);
mp.setOnErrorListener(mErrorListener);
mp.start();
this code is in PlayMedia() method, and in OnCompletionListener, I've the following code:
OnCompletionListener mCompletionListener = new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
//will be called when media player finished playing a file.
mp.release();
StartPlayingNextFile();
}
};
private void StartPlayingNextFile() {
voiceIndex++;
if (voiceIndex < q.length){
PlayMedia();
}else{
finishedPlaying = true;
}
}
When QueueListener Thread initiate MediaPlayer Thread I've used Join() on MediaPlayer thread in order not to dequeue another string from the queue and wait till the MediaPlayer finishes its business, otherwise I'll hear over lapping sounds.
Now, most of the time everything seems to be working fine but MediaPlayer sometime skips playing some files and thus MediaPlayer thread never terminates because OnCompletionListener never called and OnErrorListener never get called either, because of which Join() never releases, so I've to explicitly do it after a reasonable time has passed:
#Override
public void run() {
//record the start time
timeStart = new Date().getTime();
PlayMedia();
while (!finishedPlaying){
try {
//if a reasonable time has passed break the loop
long currentTime = (new Date().getTime() - timeStart);
long elapsedSeconds = TimeUnit.MILLISECONDS.toSeconds(currentTime);
if (elapsedSeconds > 15){
break;
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Log from LogCat can bee seen here where 5 files have been skipped between line 2 and 27 i.e.
Line 2:
01-21 01:20:18.474: V/MediaPlayerService(3240): Create new client(1854) from pid 15063, url=/mnt/sdcard/voicedata/200.wav, connId=1854
Line 27:
01-21 01:20:30.504: V/MediaPlayerService(3240): Create new client(1855) from pid 15063, url=/mnt/sdcard/voicedata/constants/bell.wav, connId=1855
files that have been skipped are:
/mnt/sdcard/voicedata/a.wav
/mnt/sdcard/voicedata/b.wav
/mnt/sdcard/voicedata/c.wav
/mnt/sdcard/voicedata/d.wav
/mnt/sdcard/voicedata/e.wav
and bell.wav is the very first file, that plays before all these files.
After rigorous testing I've found out that reducing the number of files actually improves the changes that MediaPlayer would not skip any file.