again I turn to you for a question that I have been presented.
I have a class that extend the class service and implements the class Runnable for audio playback, I'm working with a progressbar to display the progress of the reproduction of music. In the run method add the code to update the value of the progressbar, up there all right, but when I'm playing a song and jump to another without finishing the previous one, the thread that was created earlier is not destroyed, help me please?
public class PlaySongService extends Service implements MediaPlayer.OnCompletionListener, Runnable {
public static MediaPlayer mediaPlayer = new MediaPlayer();
private ProgressBar pbSong;
private final String TAG = "PlaySongService";
#Override
public IBinder onBind(Intent intent) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public void onCreate() {
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.reset();
pbSong = new WeakReference<ProgressBar>(Main.pbSong);
super.onCreate();
}
....... /* Selected song in listview*/
private void playSong(int position) {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(my_file_selected_in_listview);
mediaPlayer.prepare();
mediaPlayer.start();
pbSong.get().setProgress(0);
pbSong.get().setMax(mediaPlayer.getDuration());
new Thread(this).start();
} catch (Exception e) {
Log.e(TAG,e.getMessage());
}
}
#Override
public void run() {
int currentPosition= 0;
int total = mediaPlayer.getDuration();
while (mediaPlayer!=null && currentPosition<total) {
try {
Thread.sleep(1000);
currentPosition= mediaPlayer.getCurrentPosition();
Log.i(TAG,Thread.currentThread().toString());
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
pbSong.get().setProgress(currentPosition);
}
}
}
Threads are not "destroyed" under normal circumstances, they end when they return from the run method. This can be initiated by e.g. a boolean flag or by call to interrupt() (outside of the thread) and periodic checking of Thread.interrupted() in the thread.
Related
I am trying to play an audio using service and update seekbar item in adapter. But I am unable to get duration of audio from service. Also when scrolling recyclerview, more than one item view is updated instead of updating currently playing seekbar item. I have tried below code.
public class LocalService extends Service {
boolean mBound = false;
private final IBinder mBinder = new LocalBinder();
private String path = "";
private int timer;
private static MediaPlayer mediaPlayer = new MediaPlayer();
// Random number generator
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/**
* method for clients
*/
public int getTotalTime() {
return mediaPlayer.getDuration();
}
public int getDuration() {
// Log.v("player duration",timer+"");
try {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
Log.v("playing", (mediaPlayer.getCurrentPosition() / 1000) + "");
return mediaPlayer.getCurrentPosition() / 1000;
} else {
//Log.v("not playing", (mediaPlayer.getCurrentPosition() / 1000) + "");
return 0;
}
}catch (IllegalStateException i)
{
i.printStackTrace();
return 0;
}
catch (Exception e)
{
e.printStackTrace();
return 0;
}
}
#Override
public void onCreate() {
super.onCreate();
try {
mediaPlayer = new MediaPlayer();
} catch (Exception e) {
e.printStackTrace();
}
}
public void setAudio(String path)
{
try {
mediaPlayer.setDataSource(path);
} catch (IOException e) {
e.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void playAudio() {
Log.v("audio path", path);
try {
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.prepareAsync();
mediaPlayer.start();
timer=mediaPlayer.getDuration();
Log.v("total timer1",timer+"");
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
timer=0;
mediaPlayer.stop();
mediaPlayer.reset();
//mediaPlayer.release();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void unbindService(ServiceConnection conn) {
super.unbindService(conn);
mediaPlayer.reset();
mediaPlayer.release();
}
#Override
public void onDestroy() {
super.onDestroy();
mediaPlayer.reset();
mediaPlayer.release();
}
Service is started from adapter constructor.
Intent intent = new Intent(mContext, LocalService.class);
mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
player item click in recycler adpater
vhItemHolder.playPauseImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentlyPlayingPosition = position;
if (mBound) {
Log.v("service","bound");
//Make sure you update Seekbar on UI thread
mService.setAudio(audioDirectory+messageArrayList.get(position).getmMediaValue());
mPlayer=new MediaPlayer();
try {
mPlayer.setDataSource(audioDirectory+messageArrayList.get(position).getmMediaValue());
mPlayer.prepareAsync();
vhItemHolder.seekBar.setMax(mPlayer.getDuration());
} catch (IOException e) {
e.printStackTrace();
}
mService.playAudio();
Log.v("duration",(mService.getTotalTime())+"");
vhItemHolder.seekBar.setMax(mService.getTotalTime());
mHandler.removeCallbacks(null);
mHandler=new Handler();
((DoctorActivity)mContext).runOnUiThread(new Runnable() {
#Override
public void run() {
num = mService.getDuration();
Log.v("progress",num+"");
vhItemHolder.seekBar.setProgress((num));
vhItemHolder.runningTimerTextView.setText(convertMilliToMinutes((long)mService.getDuration()));
mHandler.postDelayed(this, 50);
}
});
}
else
{
Log.v("service","not bound");
}
Inside your adapter class:
You are making a new object of MediaPlayer (mPlayer) in viewHolder's playPauseImageView's onClickListener and calling setDataSource() method on it which sets data source for this local mPlayer object not to the mediaPlayer object of your LocalService class.
Calling mService.playAudio() and mService.getDuration() methods access your LocalService class' mediaPlayer object for which setDataSource() method is not called yet, hence must be returning wrong duration and unable to play.
You should make a change in your adapter class like below:
vhItemHolder.playPauseImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentlyPlayingPosition = position;
if (mBound) {
Log.v("service","bound");
//Make sure you update Seekbar on UI thread
mService.setAudio(audioDirectory+messageArrayList.get(position).getmMediaValue());
mPlayer = mService.mediaPlayer;// or mPlayer = LocalService.mediaPlayer; as being static object.
//rather new MediaPlayer();
try {
mPlayer.setDataSource(audioDirectory+messageArrayList.get(position).getmMediaValue());
mPlayer.prepareAsync();
vhItemHolder.seekBar.setMax(mPlayer.getDuration());
} catch (IOException e) {
e.printStackTrace();
}
mService.playAudio();
Log.v("duration",(mService.getTotalTime())+"");
vhItemHolder.seekBar.setMax(mService.getTotalTime());
mHandler.removeCallbacks(null);
mHandler=new Handler();
((DoctorActivity)mContext).runOnUiThread(new Runnable() {
#Override
public void run() {
num = mService.getDuration();
Log.v("progress",num+"");
vhItemHolder.seekBar.setProgress((num));
vhItemHolder.runningTimerTextView.setText(convertMilliToMinutes((long)mService.getDuration()));
mHandler.postDelayed(this, 50);
}
});
}
else
{
Log.v("service","not bound");
}
I tried almost everything found on the internet and I can't stop the media player once it starts. I'm using broadcast receiver and I'm controlling the media player using SMS. Here is my code.
public class Receiver extends BroadcastReceiver{
String body;
String address;
public static final String SMS_EXTRA_NAME="pdus";
MediaPlayer mp = new MediaPlayer();
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
SharedPreferences obj1=context.getSharedPreferences("mypref", Context.MODE_PRIVATE);
String newstring=obj1.getString("key1", null);
String name=newstring;
Bundle bund=intent.getExtras();
String space="";
if(bund!=null)
{
Object[] smsExtra=(Object[])bund.get(SMS_EXTRA_NAME);
for(int i=0;i<smsExtra.length;i++)
{
SmsMessage sms=SmsMessage.createFromPdu((byte[])smsExtra[i]);
body=sms.getMessageBody().toString();
address=sms.getOriginatingAddress();
if(body.equals("ON"))
{
if(mp.isPlaying())
{
mp.stop();
}
try {
mp.reset();
AssetFileDescriptor afd;
afd = context.getAssets().openFd("file.mp3");
mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
mp.prepare();
mp.start();
mp.setLooping(true);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
else if(body.equals("OFF"))
{
if (mp.isPlaying()==true||mp!=null)
{
try{
mp.stop();
mp.release();
} catch(Exception e){
System.out.println("Exception"+e);
}
}
}
}
}
}
}
The media player is turning on when I send "ON", but it won't turn off. And yes I have given the required permissions in the Manifest file.
The BroadcastReciever it stays alive for around 9 seconds, you should not create big operation in it. However, you can let it start an operation like start acitivty or service and there you play a track or start download a file ...etc
If you want to only start a player and no need for user interaction, I suggest that you start a service and there you play your what you want.
I spent a lot of time studying this problem, and found out that:
The problem here is that I create a MediaPlayer inside a thread that is managed by the IntentService. And at the time of starting playback the thread is no longer valid.
So the way out is:
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);
It helped me stop the mediaplayer.
In my app I have an audio player Activity which simply shows the current song title and album cover art together with a MediaController. I also have a Service which implements MediaController.MediaPlayerControl and which uses a MediaPlayer for playback.
In general everything works fine - the Activity binds to the Service and sets the MediaController to control the MediaPlayer.
The problem comes when starting a new song but ONLY when the MediaController is shown on the screen. If it is hidden or even if I leave the Activity using BACK and return to the home screen, the 'playlist' is processed without a problem. If the MediaContoller is visible, however, the player dies silently and the seek / progress bar shows a start and end time of 466:36:07 and logcat shows...
attempt to call getDuration without a valid mediaplayer
I don't get an unhandled exception stacktrace in logcat - just that message and MediaPlayer simply stops playing. I've Googled for the error and get nearly 3000 hits (most of which are for questions here on SO) but I can't find a situation which matches mine.
Relevant Activity code...
public class AudioPlayerActivity extends Activity
implements OnKeyListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_player);
// Find views here
controller = new MyMediaController(this);
controller.setAnchorView(ll);
controller.setOnKeyListener(this);
}
#Override
protected void onResume() {
super.onResume();
mBound = bindService(new Intent(this, MusicPlayerService.class), mConnection, Context.BIND_AUTO_CREATE);
try {
getCurrentItem();
mediaFilename = currentItem.getTrackName();
coverArtPath = currentItem.getMediaArtUrl();
updateTrackDetails();
Intent playerServiceIntent = new Intent(this, MusicPlayerService.class);
startService(playerServiceIntent);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public void showMediaController() {
// Called by onTouchEvent(...)` when the screen is touched
if (mBound) {
controller.setMediaPlayer(mService);
controller.setEnabled(true);
controller.show(0);
}
}
}
The Service has the following code...
public class NDroidTEMusicPlayerService extends Service
implements MediaPlayer.OnPreparedListener,
MediaPlayer.OnCompletionListener,
MediaPlayer.OnErrorListener,
AudioManager.OnAudioFocusChangeListener,
MediaPlayerControl {
private void setupMediaPlayer() {
...
try {
mMediaPlayer.setDataSource(trackUrl);
mMediaPlayer.prepareAsync();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onCompletion(MediaPlayer mp) {
stop();
mp.reset();
currentItem = playList.getNextItem(isRepeating);
Intent i = new Intent(MYAPP.ACTION_MYAPP_UPDATE_PLAY_VIEW);
i.putExtra("current_item", currentItem);
sendBroadcast(i);
setupMediaPlayer();
}
#Override
public int getDuration() {
int duration = 0;
if (mMediaPlayer != null)
duration = mMediaPlayer.getDuration();
return duration;
}
}
I am implementing a game, and I want to use one and only one MediaPlayer instance, running in a separate thread from the UI. Each audio file is sent to a FIFO queue, and played immediately if no file is playing, or as soon as the previous file in the queue is finished.
I have implemented a singleton class to support this as follows:
public class ThePlayer {
private static ThePlayer instance = null;
private Context context;
Thread playerThread;
BlockingQueue<Integer> listIDs = new LinkedBlockingQueue<Integer>();
MediaPlayer player;
Integer playID;
private ThePlayer() {
player = null;
playerThread = new Thread(new Runnable() {
//Object locker = new Object();
public void run() {
while (true) {
try {
playID = listIDs.take(); // blocks if list empty
Log.d(this.getClass().getName(), "Took ID 0x" + Integer.toHexString(playID));
player = MediaPlayer.create(context, playID);
player.setOnCompletionListener(
new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mp.release();
mp = null;
synchronized (playerThread) {
Log.d(this.getClass().getName(), "Finished playing 0x"+Integer.toHexString(playID));
playerThread.interrupt();
}
}
});
player.start();
Log.d(this.getClass().getName(), "Started playing 0x"+Integer.toHexString(playID));
synchronized (this) {
Log.d(this.getClass().getName(), "Now wait to finish");
wait(); // until finished playing
}
} catch (InterruptedException ie) {
Log.d(getClass().getName(), "Player thread wait interrupted");
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
playerThread.setName("PlayerThread");
playerThread.start();
}
public static ThePlayer getInstance()
{
if(instance == null)
{
instance = new ThePlayer();
}
return instance;
}
public void play(Context _ctx, int id) {
context = _ctx;
listIDs.add(id);
Log.d(this.getClass().getName(), "Added ID 0x" + Integer.toHexString(id));
}
public void stop() {
//TODO: implement
}
}
In general, this works well, but one particular file fails to play in an inconsistent manner. In other words, in most cases, it does not play, but occasionally it does. All the log entries indicate that the file is queued, the MediaPlayer is allocated and started, and that the onCompletion function is called. At no point is an exception thrown.
This happens on a Motorola Defy+ phone and various emulator configurations. It does not happen on my Samsung Galaxy Tab II 10.1, and my guess is that is a function of more capable hardware, or possibly the more advanced version of Android.
I am starting a thread when invoking a method to play an audio file.
The code runs ok the first time but when I call the play method again I need the thread to start as if it were being called the first time. I have tried to interrupt the thread and even stop it but nothing seems to work.
How can I properly restart the thread?
Here is some code to help explain.
Global variable
private Thread thread1;
Thread code:
thread1 = new Thread(new Runnable()
{
#Override
public void run()
{
try {
int i=0;
final TextView timeDuration = (TextView) findViewById(R.id.timeDisplay);
final SeekBar seekBar = (SeekBar)findViewById(R.id.seekBar1);
while (running)
{
info();
j = 0;
while(i>0 && running)
{
while(j<duration && running && (playStatus.getValue() == "TRANSITIONING" || playStatus.getValue() == "PLAYING"))
{
seekBar.setMax((int) duration);
seekBar.setProgress(0);
runOnUiThread(new Runnable()
{
public void run()
{
System.out.println("PLAYBACK STATUS: "+playStatus.getValue());
timeDuration.setText(""+(j+1));
seekBar.setProgress(j+1);
if(j==(duration-1))
{
setRunning(false);
}
}
});
Thread.sleep(1 * 1000);
j++;
if(seekBar.getProgress() == seekBar.getMax())
{
runOnUiThread(new Runnable()
{
public void run()
{
playButton.setVisibility(View.VISIBLE);
pauseButton.setVisibility(View.GONE);
timeDuration.setText(""+"0");
seekBar.setProgress(0);
flag = false;
System.out.println("J VALUE 1: = "+j);
duration = 0;
setRunning(false);
}
});
}
}
}
j = 0;
i++;
Thread.sleep(1 * 1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
play();
This code works fine and plays the track. It then resets the seekbar and awaits for the play method to be called again.
public void play()
{
try
{
thread1.start();
}
catch(Exception e)
{
return;
}
}
Here is the setRunning method recommended to me.
public void setRunning(boolean b)
{
this.running = b;
}
If anyone know of a solution to this problem I would really appreciate it.
Threads are not supposed to be stopped manually. You should use a boolean instead of true in your while loop, and put the boolean to false through a setter when you want to stop:
private boolean running;
#Override
public void run(){
running = true;
while(running) {...}
}
public void setRunning(boolean b){
this.running = b;
}
To restart your player you need to prepare again.
public void restartAudio() {
Log.e(TAG, "restartAudio");
if (mp.isPlaying()) {
mp.seekTo(0);
}else{
mp.stop();
try {
mp.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
When using extra boolean flag to controll thread's while loop don't forget to use volatile modifier on it:
private volatile boolean running;
or put appropriate synchronization.
Apart from that, I'd think about using Thread.isInterrupted() method instead of the additional 'running' flag:
http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#isInterrupted()
here's why:
https://stackoverflow.com/a/3196058/1350225