I have created a runnable thread to change/swipe the View Pager position through auto-looping in homepage.
In my app, I want to stop this thread when logout button is clicked.
I have tried removecallback method but still the app crashes sometimes.
Any other solution?
Below is my Code:
final Handler offers1Handler = new Handler();
Runnable offers1Runnable;
private void setLooping() {
offers1Runnable = () -> scrollPager(binding.contentHome.offers1ViewPager);
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
offers1Handler.postDelayed(offers1Runnable, 1000);
}
},500, 3000);
}
}
private void onLogout(){
offers1Handler.removeCallbacks(offers1Runnable);
}
if you want to cancel the timer by yourself, you can use cancel method.
the Runnable is not a thread, it just has a method run. the Timer is A facility for threads, which to schedule tasks for future execution in a background thread. if you want to cancel, you actually want to cancel the Timer. As the document describes:
If a caller wants to terminate a timer's task execution thread rapidly, the caller should invoke the timer's cancel method.
if you call the cancel method. its execution thread terminates gracefully, and no more tasks may be scheduled on it.
Related
I'm using Android Studio and emulator android 4.1.2.
My code
Timer timer = new Timer ();
timer.schedule(new TimerTask() {
#Override
public void run() {
myRun();
}
},10000,10000);
result in "unfortunately, app has stopped", however I found out the code
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
myRun();
}
}, 2000);
runs ok and displays as I expect.
What is the inner difference between the two?
P.S.
public void myRun () {
myView.removeAllViews();
drawView = new DrawView(myContext, myView);
myView.addView(drawView);
}
I'm trying to perpetually update a custom view until user cancels it. Just cycle
while (myRun) {
results in emulator becoming unresponsive to even back button, running that update in second thread
new Thread(new Runnable() {
#Override
public void run() {
while (myRun) {
myView.post(new Runnable() {
public void run() {
myView.removeAllViews();
drawView = new DrawView(myContext, myView);
myView.addView(drawView);
}
});
}
}
}).start();
results in same (interestingly to me, if I run debug with breakpoint on while in second thread, screen updates as I expect many times, however running w/out debugging does not update screen).
Timer executes its tasks on a separate thread that is used only for serving tasks created by this particular timer. Handler runs its task on its Looper's thread which may or may not be a UI thread. Generally speaking there's no much difference between this two classes if you use Handler on a separate thread. But it's more common in Android to use Handler and HandlerThread.
If you need to interact with UI, you'd better use Handler.
This is an interesting question and answer lies in Thread/GUI policy that android follows.
As we know, UI runs on main thread. Timer creates a different thread and android does not allow to update UI in a different thread. Why?
Suppose, you have started a thread in your activity that updates a TextView and while the thread is running you move to some other app. Now, main thread no longer exists and when the other thread tries to update the TextView it is not able to find that TextView. As a result, we see a crash.
Now let me come to the difference between TimerTask and Handler.
TimerTask creates a new thread, waits for the time specified and then executes run() method in the same thread. On the other hand, Handler creates a new thread, waits for specified duration then returns to main thread and executes run() method on MAIN thread(if handler is on main thread). Hence, it works fine.
However you can do it with timer too.
See the code below:
final Runnable setRunnable = new Runnable() {
public void run() {
myView.removeAllViews();
drawView = new DrawView(myContext, myView);
myView.addView(drawView);
}
};
TimerTask task = new TimerTask(){
public void run() {
getActivity().runOnUiThread(setRunnable);
}
};
Timer timer = new Timer();
timer.schedule(task, 1000);
In this thread you are setting a runnable to run on UI thread after timer's duration.
I was wondering how the TimerTask is working with threads.
For example, I've got a code that executes a TimerTask, which has a 'run' method which will run on the UI Thread.
class looper extends TimerTask {
public looper() {
}
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
timer.schedule(new looper(), new Date(new Date().getTime() + 100));
}
});
}
}
and the timer would start like this:
timer = new Timer();
timer.schedule(new looper(), new Date());
Will the TimerTask create a new thread? if so, how does runOnUiThread will work? will it move the code to the UI thread?
I've tried to eliminate the need to call the TimerTask again (timer.schedule) and just use an infinite loop inside the run to make calculations - but that would block the UI thread and the app will not respond.
P.S - I must have the code run on the UI thread, because it has to update the UI.
So, what's going on here?
About your questions:
runOnUiThread from docs:
Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.
The code at TimerTask it is executed on a different thread, so you should call runOnUiThread to execute code.
You are doing it right, but why are you re-creating the timertask at the run method ? Consider using scheduleAtFixedTime , the 100 will be the period and the delay 0.
The run method then will execute the task it is supposed to do.
runOnUiThread(new Runnable() {
#Override
public void run() {
//do whatever
}
});
scheduleAtFixedTime
*Updated:
If its an update to a view it is better to use handler.postDelayed, see an example here it will be executed after a delay on the UI thread.
I have written a handler that calls the method every time interval. I want to remove that handler in on destroy(). The code i use as follows, In Oncreate()
private final Handler _handler = new Handler();
public int DATA_INTERVAL = 30 * 1000;
Runnable getData;
getData = new Runnable()
{
#Override
public void run()
{
recieveData();
}
};
_handler.postDelayed(getData, DATA_INTERVAL);
and in ondestroy(), i use,
_handler.removeCallbacks(getData);
But removecallbacks not work. It calls after exiting the activity.
removeCallbacks(Runnable r):
Remove any pending posts of Runnable r that are in the message queue.
so removeCallbacks(..) only stops pending messages (Runnables) not currently running runnable so if you want to stop currently running Runable then use a Boolean varaible for Stoping Thread when user Exit from your app.
see this post for removeCallbacks not stopping runnable
You are not showing the most important part of the code which is the receiveData method. As you said you are running the task periodically, you must be calling again postDelayed from inside that method to reschedule the task. Probably background threads involved as you cannot do networking on the main thread.
You most likely have a race condition when exiting the Activity. onDestroy runs first and then the task is posted again.
You should use
handler.removeCallbacksAndMessages(null);
Then all handler callbacks will removed.
How can I reschedule a timer. I have tried to cancel the timer/timertask and and schedule it again using a method. But its showing an exception error:
Exception errorjava.lang.IllegalStateException: TimerTask is scheduled already
Code I have used it :
private Timer timer = new Timer("alertTimer",true);
public void reScheduleTimer(int duration) {
timer.cancel();
timer.schedule(timerTask, 1000L, duration * 1000L);
}
If you see the documentation on Timer.cancel() you'll see this:
"Cancels the Timer and all scheduled tasks. If there is a currently running task it is not affected. No more tasks may be scheduled on this Timer. Subsequent calls do nothing."
You'll need to initialize a new Timer when you are rescheduling:
EDIT:
public void reScheduleTimer(int duration) {
timer = new Timer("alertTimer",true);
timerTask = new MyTimerTask();
timer.schedule(timerTask, 1000L, duration * 1000L);
}
private class MyTimerTask extends TimerTask {
#Override
public void run() {
// Do stuff
}
}
In fact, if you look in the cancel method javadoc, you can see the following thing :
Does not interfere with a currently executing task (if it exists).
That tells the timer "ok, no more tasks now, but you can finish the one you're doing". I think you'll also need to cancel the TimerTask.
#Eric Nordvik answer is running fine.
One thing we can do is to cancel previous timer events execution
public void reScheduleTimer(int duration) {
// Cancel previous timer first
timer.cancel();
timer = new Timer("alertTimer",true);
timerTask = new MyTimerTask();
timer.schedule(timerTask, 1000L, duration * 1000L);
}
Actually you can use purge() so you don't have to initialize a new Timer.
public int purge ()
Added in API level 1
Removes all canceled tasks from the task queue. If there are no other references on the tasks, then after this call they are free to be garbage collected.
Returns the number of canceled tasks that were removed from the task queue.
I am currently trying to set up a WiFi Scan in my Android application that scans for WiFi access points every 30 seconds.
I have used Timer and TimerTask to get the scan running correctly at the intervals which I require.
However I want to be able to stop and start the scanning when the user presses a button and I am currently having trouble stopping and then restarting the Timer and TimerTask.
Here is my code
TimerTask scanTask;
final Handler handler = new Handler();
Timer t = new Timer();
public void doWifiScan(){
scanTask = new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
wifiManager.scan(context);
Log.d("TIMER", "Timer set off");
}
});
}};
t.schedule(scanTask, 300, 30000);
}
public void stopScan(){
if(scanTask!=null){
Log.d("TIMER", "timer canceled");
scanTask.cancel();
}
}
So the Timer and Task start fine and the scan happens every 30 seconds however I cant get it to stop, I can stop the Timer but the task still occurs and scanTask.cancel() doesn't seem to work either.
Is there a better way to do this? Or am I missing something in the Timer/TimerTask classes?
You might consider:
Examining the boolean result from calling cancel() on your task, as it should indicate if your request succeeds or fails
Try purge() or cancel() on the Timer instead of the TimerTask
If you do not necessarily need Timer and TimerTask, you can always use postDelayed() (available on Handler and on any View). This will schedule a Runnable to be executed on the UI thread after a delay. To have it recur, simply have it schedule itself again after doing your periodic bit of work. You can then monitor a boolean flag to indicate when this process should end. For example:
private Runnable onEverySecond=new Runnable() {
public void run() {
// do real work here
if (!isPaused) {
someLikelyWidget.postDelayed(onEverySecond, 1000);
}
}
};
using your code, instead of
scanTask.cancel();
the correct way is to cancel your timer (not timerTask):
t.cancel();
The Android documentation says that cancel() Cancels the Timer and all scheduled tasks. If there is a currently running task it is not affected. No more tasks may be scheduled on this Timer. Subsequent calls do nothing. Which explains the issue.