My Timer doesn't stop running if I cancel it!
The Timer only stops if I shut down the whole app!
I don't know why the Timer is not cancelled. If I print out every try on cancelling the Timer I get hundrets of lines but the Timer does not stop!
My Class:
public class PlayActivity extends AppCompatActivity
implements View.OnClickListener, SeekBar.OnSeekBarChangeListener, MediaplayerEvent {
//region Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Initialize_Layout();
Initialize_Objects();
}
#Override
protected void onResume() {
super.onResume();
MusicService.setMediaPlayerEvent(this);
txvSongtitle.setText(serviceInterface.MP_getActualSong().getTitle());
Start_Timer();
}
#Override
protected void onPause() {
timer.cancel();
MusicService.clearMediaPlayerEvent();
super.onPause();
}
#Override
public boolean onSupportNavigateUp() {
finish();
return super.onSupportNavigateUp();
}
//endregion
//region Methods
private void Start_Timer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if (serviceInterface.MP_isPlaying()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
seekBar.setMax(serviceInterface.MP_getDuration());
seekBar.setProgress(serviceInterface.MP_getCurrentPosition());
}
});
}
else {
runOnUiThread(new Runnable() {
#Override
public void run() {
timer.cancel();
}
});
}
}
}, 0, 200);
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
serviceInterface.MP_seekTo(progress);
Start_Timer();
}
}
//endregion
}
I hope you can help me!
Thanks!
I would suggest using a Thread instead of a Timer. Your Start_Timer()code would change to something like the following:
private Thread mTimerThread;
...
private void Start_Timer() {
mTimerThread = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
if (serviceInterface.MP_isPlaying()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
seekBar.setMax(serviceInterface.MP_getDuration());
seekBar.setProgress(serviceInterface.MP_getCurrentPosition());
}
});
} else {
interrupt();
}
Thread.sleep(200);
}
} catch (InterruptedException e) {
}
}
}
mTimerThread.start();
}
Threads are more efficient and lightweight and perfect for your needs. Plus, by setting the Thread to a global variable, you can make sure to call mTimerThread.interrupt(); during Android lifecycle events, such as onPause().
I hope this fixes your issue. Remember, the Java Thread is your friend!
You're creating and starting a new timer the user moves the seekbar (in onProgressChanged()). That also means you lose the reference to the old one. When isPlaying turns false, all the timers will try to cancel timer -- which only references the most recent one.
Related
I have a thread in my callback function as follows:
#Override
public void onConnectError(final BluetoothDevice device, String message) {
Log.d("TAG","Trying again in 3 sec.");
runOnUiThread(new Runnable() {
#Override
public void run() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something
}
}, 2000);
}
});
}
I will to close the the above thread when I press the back button or onDestroy. How can I do it. Thank you
#Override
public void onBackPressed() {
// Close or distroy the thread
}
#Override
public void onDestroy() {
// Close or distroy the thread
}
Please do this like
private Handler handler;
private Runnable runnable;
#Override
public void onConnectError(final BluetoothDevice device, String message) {
Log.d("TAG","Trying again in 3 sec.");
runOnUiThread(new Runnable() {
#Override
public void run() {
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
//Do something
}
};
handler.postDelayed(runnable, 2000);
}
});
}
and
#Override
public void onBackPressed() {
if (handler != null && runnable != null) {
handler.removeCallbacks(runnable);
}
}
and same in onDestroy();
I'm mostly use thread in this way.See its independent in activity
public class TestActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.abc);
holdConnectionHandler.sendEmptyMessage(0);
}
Handler holdConnectionHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
// do some work
holdConnectionHandler.sendEmptyMessageDelayed(0, 10 * 1000);
}
};
#Override
public void onDestroy() {
super.onDestroy();
holdConnectionHandler.removeCallbacksAndMessages(null);
// or
holdConnectionHandler.removeMessages(0);
}
}
Thanks hope this will help you
I have tried overriding onBackPressed to not only finish the current activity but also to interrupt the thread as I am calling an intent to the next activity within. When I pressed back before, the splash activity finished, but the thread kept running and call the intent to the next activity. Now that I've include the thread interruption onBackPressed,the app crashes when I press back. What am I doing wrong?
skipscreen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
SharedPreferences savestate = getSharedPreferences("skip", MODE_PRIVATE);
savestate.edit().putBoolean("skip", true).apply();
skip= true;
homeintent();
}
});
Thread splashthread = new Thread() {
#Override
public void run() {
try {
sleep(3000);
if (!skip) { homeintent();}
} catch (InterruptedException Interrupt) {
Interrupt.printStackTrace();
}
}
};
splashthread.start();
}
private void homeintent() {
Intent i = new Intent(splash.this, home.class);
startActivity(i);
finish();
}
#Override
public void onBackPressed() {
splashthread.interrupt();
finish();
}
}
UPDATED: (This solution has worked for me).
skipscreen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
SharedPreferences savestate = getSharedPreferences("skip", MODE_PRIVATE);
savestate.edit().putBoolean("skip", true).apply();
skip= true;
homeintent();
}
});
Thread splashthread = new Thread() {
#Override
public void run() {
try {
sleep(3000);
if (!skip) { homeintent();}
} catch (InterruptedException Interrupt) {
Interrupt.printStackTrace();
}
}
};
splashthread.start();
}
private void homeintent() {
if (!ThreadInterrupted) {
Intent i = new Intent(splash.this, home.class);
startActivity(i);
}
finish();
}
#Override
public void onBackPressed() {
SharedPreferences savestate= getSharedPreferences("ThreadInterrupted", MODE_PRIVATE);
savestate.edit().putBoolean("ThreadInterrupted", true).apply();
ThreadInterrupted = true;
homeintent();
}
}
To give you a succint answer as well as to explain how to do this in the future,take a look at the Handler class and the Handler.postDelayed() method.
private Handler homeIntenthandler;
#Override
protected void onCreate(){
//other things
skipscreen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
SharedPreferences savestate = getSharedPreferences("skip", MODE_PRIVATE);
savestate.edit().putBoolean("skip", true).apply();
skip= true;
homeintent();
}
});
//initialise handler and set timer;
homeIntenthandler= new Handler();
homeIntenthandler.postDelayed(new Runnable() {
#Override
public void run() {
homeintent();
}
},3000);
}
}
private void homeintent() {
Intent i = new Intent(splash.this, home.class);
startActivity(i);
finish();
}
#Override
public void onBackPressed() {
if(homeIntenthandler!=null)
homeIntenthandler.removeCallbacksAndMessages(null)
finish();
}
}
the Handler.removeCallbacksAndMessages(null) is based on the documentation given for the same
When you call interrupt() method on your thread, while in sleep, an InterruptedException will be thrown.
You can check inside your thread weather it should continue:
Thread splashthread = new Thread() {
#Override
public void run() {
while (shouldContinue) {
doSomeWork();
}
}
};
You can then change shouldContinue to false in your onBackPressed method:
#Override
public void onBackPressed() {
shouldContinue = false;
finish();
}
Udi I has answered your question.
But i refer you to use TimerTask and Timer instead of Thread.
Timer timer;
TimerTask timertask;
timer = new Timer();
timertask = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
homeintent();
}
});
}
};
timer.schedule(timertask, 3000);
#Override
public void onBackPressed() {
if(timertask!=null){
timertask.cancel();
}
if(timer!=null){
timer.cancel();
}
finish();
}
You can create a volatile boolean check to stop thread from progressing, see following code:
private volatile boolean cancelled = false;
Thread splashthread = new Thread() {
#Override
public void run() {
if ( cancelled ) {
return; // or handle this however you want
}else{
// do what ever you want to do here
}
}
};
splashthread.start();
And in onBackPressed do something like this:
#Override
public void onBackPressed() {
cancelled = true;
finish();
}
The volatile keyword is recommended because, generally, the thread doing the cancelling not necessarily will be the same thread that is being cancelled. Marking boolean check as volatile ensures that the thread checking the flag will see the up to date value.
The following code is from Beginning Android 3, Chapter 20. When the phone is rotated, a new activity will be created and onStart() will be called, and so bar.setProgress(0) is called. However, I don't see the bar's progress is back to the beginning. Why not?
public class HandlerDemo extends Activity {
ProgressBar bar;
Handler handler=new Handler() {
#Override
public void handleMessage(Message msg) {
bar.incrementProgressBy(5);
}
};
AtomicBoolean isRunning=new AtomicBoolean(false);
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
bar=(ProgressBar)findViewById(R.id.progress);
}
public void onStart() {
super.onStart();
bar.setProgress(0);
Thread background=new Thread(new Runnable() {
public void run() {
try {
for (int i=0;i<20 && isRunning.get();i++) {
Thread.sleep(1000);
handler.sendMessage(handler.obtainMessage());
}
} catch (Throwable t) {
// just end the background thread
}
}
});
isRunning.set(true);
background.start();
}
public void onStop() {
super.onStop();
isRunning.set(false);
}
}
Try using this code
#Override
protected void onPause() {
super.onPause();
isRunning.set(false);
bar.setProgress(0);
}
This is my code on Activity to Invalidate the canvas it is not invalidating. Means onDraw() is not getting called even once;
public GraphView view;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
view = GraphView(this,null);
runplotTimer();
}
public void runplotTimer()
{
Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
InvalidateTimer();
}
},1000,40);
}
public void InvalidateTimer()
{
this.runOnUiThread(new Runnable() {
#Override
public void run()
{
//Log.d(ALARM_SERVICE, "Timer of 40 miliseconds");
view.InvalidateGraph();
}
});
}
on View class this is method which is gettting called from Activity. other OnDraw declaration is same as required.
public void InvalidateGraph()
{
m_bCalledPlotRealTimeGraph = true;
invalidate(chanX_count1, 0, chanX_count1+7, graphheight);
}
Any help please ?
You are attempting to make changes to the View on a Timer Thread, which will not work. You need to call invalidate on the main (UI) thread:
((Activity) view.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
invalidate(chanX_count1, 0, chanX_count1+7, graphheight);
}
});
you need to Start the Timer
Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
InvalidateTimer();
}
},1000,40);
t.start()
Instead of Timer use Handler.
class UpdateHandler implements Runnable {
#Override
public void run(){
handler.sendEmptyMessageAtTime(0, 1000);
handler.postDelayed(this, 1000);
}
}
private Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//Call your draw method
}
}
};
Inside onCreate and onResule write
if( mupdateTask == null )
mupdateTask = new UpdateHandler();
handler.removeCallbacks(mupdateTask);
Call your handler using
handler.postDelayed(mupdateTask, 100);
I have a handler that I am using as follows:
handler.postDelayed(Play, 1000);
when my application onPause() is called before this is done, I need to pause it and tell it not to perform the "postDelayed" until I resume.
is this possible, or is there an alternative way?
My problem is that when onPause() is called I pause the audio (SoundManager), but if this handler.postDelayed is called after that, the audio will not be paused and will continue to play with my application in the background.
#Override
public void onPause()
{
Soundmanager.autoPause()
}
but then the postDelayed after 1000ms starts the audio playing again.
You need to subclass Handler and implement pause/resume methods as follows (then just call handler.pause() when you want to pause message handling, and call handler.resume() when you want to restart it):
class MyHandler extends Handler {
Stack<Message> s = new Stack<Message>();
boolean is_paused = false;
public synchronized void pause() {
is_paused = true;
}
public synchronized void resume() {
is_paused = false;
while (!s.empty()) {
sendMessageAtFrontOfQueue(s.pop());
}
}
#Override
public void handleMessage(Message msg) {
if (is_paused) {
s.push(Message.obtain(msg));
return;
}else{
super.handleMessage(msg);
// otherwise handle message as normal
// ...
}
}
//...
}
Have you tried with:
#Override
public void onPause()
{
handler.removeCallbacks(Play);
Soundmanager.autoPause()
}
Ger
Modifying the answer given by CpcCrunch. There handleMessage not worked for me, so instead of it using dispatchMessage. Note: Below code is written in Kotlin:
class CustomHandler: Handler() {
var s = Stack<Message>()
var is_paused = false
#Synchronized
fun pause() {
is_paused = true
}
#Synchronized
fun resume() {
is_paused = false
while (!s.empty()) {
sendMessageAtFrontOfQueue(s.pop())
}
}
override fun dispatchMessage(msg: Message?) {
if (is_paused) {
s.push(Message.obtain(msg))
return
} else {
super.dispatchMessage(msg)
}
}
}
public class YourActivity extends AppCompatActivity {
private static boolean handlerflag=false;
private Handler handler;
private Runnable runnable;
private int myind=0,index=0,count=0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activtiy);
//oncreate exe only
handlerflag=true;
handler = new Handler();
startyourtime(0);
}
private void startyourtime(int a) {
myind=0;
for (index=a; index<10 ;index++) {
myind++;
runnable=new Runnable() {
count++;
#Override
public void run() {
//your code here
}
};handler.postDelayed(runnable, Constants.TIME_LIMIT * myind);
}
#Override
protected void onPause() {
super.onPause();
handlerflag=false;
handler.removeCallbacksAndMessages(null);
}
#Override
protected void onResume() {
super.onResume();
if(!handlerflag)
{
startyourtime(count);
}
}
}
I came up with an alternative to CpnCrunch when wanting to pause/resume Runnables in a queue. To have methods that has been called whilst still connecting and is offline, once online, resume the queue and all runnables are executed.
Instead of using Handler, use ExecutorService:
public class ExecutorQueueService extends ThreadPoolExecutor {
private Stack<Runnable> runnables = new Stack<>();
private boolean paused = false;
public ExecutorQueueService() {
super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
public synchronized void pause() {
paused = true;
}
public synchronized void resume() {
paused = false;
while (!runnables.empty()) {
execute(runnables.pop());
}
}
public synchronized boolean isPaused() {
return paused;
}
#Override
public void execute(Runnable runnable) {
if (paused) {
runnables.push(runnable);
} else {
super.execute(runnable);
}
}
}
Using it is similar to Handler, but instead of post(runnable), use execute(runnable)