Android. How to stop a handler once it executed a runnable once - android

EDIT: Code works now. I ended up calling loadingIllusionLoader() from within createDialog()...
I am trying to get a 'fake' progress bar to show up once a user presses a button. I want the progress bar to appear for some random time ~ 2000ms and then have a Dialog box appear as well as hiding the progress bar (because it 'loaded').
I was told to try and use handler since Thread.sleep locks up the UI, which I dont want to really do. However, once I execute the code below, it runs the handler's postDelayed function and a new Dialog box appears every moment or so... the handeler is executing itself over and over again. How do I STOP the handler. the removeCallbacksAndMessages function on the handler was an option, but Im not sure how to exactly STOP the Dialog boxes from opening.
public void loadingIllusionLoader()
{
ProgressBar theCircleLoader = (ProgressBar) findViewById(R.id.progressBar2);
theCircleLoader.setVisibility(View.VISIBLE);
int timeToRest = (int) (Math.random() * 1000) + 1500;
final Handler newHandle = new Handler();
newHandle.postDelayed(new Runnable() {
#Override
public void run() {
createDialog();
hidingIllusionLoader();
newHandle.removeCallbacksAndMessages(null);
}
}, timeToRest);
}
public void hidingIllusionLoader()
{
ProgressBar theCircleLoader = (ProgressBar) findViewById(R.id.progressBar2);
theCircleLoader.setVisibility(View.INVISIBLE);
}

I think you'd rather want to use a CountDownTimer:
CountDownTimer timer = new CountDownTimer( 10000, 1000 ) {
#Override public void onTick( long millisUntilFinished ) {
theCircleLoader.setProgress( theCircleLoader.getProgress() - 1 );
}
#Override public void onFinish() {
theCircleLoader.setVisibility(View.INVISIBLE);
}
};
EDIT: almost forgotten:
timer.start();
EDIT2:
after looking at your code, I suggest you modify it so:
Random rnd = new Random();
int progressBarMax = rnd.nextInt( 10 ) + 1; // 10 - change it the way you like
int timeToRest = progressBarMax * 500;
theBarLoader.setMax( progressBarMax );
theBarLoader.setProgress( 0 );
CountDownTimer theTimer = new CountDownTimer(timeToRest, 500)
{
#Override public void onTick( long millisUntilFinished ) {
theBarLoader.setProgress( theCircleLoader.getProgress() + 1 );
}
#Override public void onFinish() {
theCircleLoader.setVisibility(View.INVISIBLE);
// theBarLoader.setVisibility(View.INVISIBLE);
createDialog();
}
};
theTimer.start();

Related

Android view doesn't update when trying to set values over time

I got a fragment which got a control called RingProgress which is simply a ring that fills itself according to a percentage value given. For example, if I do:
ringProgress.setProgress(20);
It means that 20% of the ring will now be filled.
What I'm trying to do is to animate the ring being filled over a few seconds. So what I've tried to do is this:
#Override
public void onResume()
{
super.onResume();
HandlerThread handlerThread = new HandlerThread("countdown");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable()
{
#Override
public void run()
{
final Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask()
{
int totalSeconds = secondsToStart + minutesToStart * 60;
int secondsPassed = 0;
#Override
public void run()
{
if(secondsPassed == totalSeconds)
{
timer.cancel();
}
final int currentProgress = (secondsPassed / totalSeconds) * 100;
secondsPassed++;
getActivity().runOnUiThread(new Runnable()
{
#Override
public void run()
{
mRingProgressBar.setProgress(currentProgress);
}
});
}
}, 0, 1000);
}
});
}
The problem is that the update of the ring is not shown until the time is up. For example, if I set it for 5 seconds then when the fragment loads the ring is set to 0, then nothing happens for 5 seconds and then the ring is full with 100% all at once..
How can I start this animation properly?
I guess the problem is with
final int currentProgress = (secondsPassed / totalSeconds) * 100;
secondsPassed / totalSeconds return int value so it will be 0 or 1 only. And you multiply it to 100.
You have to use float or double instead
something like
final int currentProgress = Math.round(((float) secondsPassed)/((float) totalSeconds)*100f);
On this line:
Handler handler = new Handler(handlerThread.getLooper());
You are trying to get the looper from a handlerThread. But how sure you are the looper has already been initialized?
From the documentation of getLooper()
This method returns the Looper associated with this thread. If this thread not been started or for any reason is isAlive() returns false, this method will return null. If this thread has been started, this method will block until the looper has been initialized.
onLooperPrepared() is the callback, where you can be sure, that the Looper has been initialized, and therefore you can construct logics on that.
Thus, what you have to do, is to subclass HandlerThread and create appropriate logics from onLooperPrepared() callback.
Here's a nice post which will help you out. See implementation of MyWorkerThread class there.
Instead of using a handler, you could use a property animator as follows:
ObjectAnimator.ofInt(mRingProgressBar, "progress", 0, 100)
.setDuration(totalSeconds * 1000) //time is in miliseconds
.start();
This will find a method setProgress() in your mRingProgressBarand set the value according to the limits given. In the example above, 0 to 100.
You can read more about it here
Since you want to run on a different thread, you can use this handler in the top of the class:
private int progress = 0;
Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {
#Override
public void run() {
ringProgress.setProgress(progress);
progress += 20;
if (progress == 100) { //clear??
}
timerHandler.postDelayed(this, 1000);
}
};
In inCreate set the max:
ringProgress.setMax(100);
This will complete the animation within 5 seconds, then you can clear the animation. If you want smaller increments, change the line below (update every tenth of a second), and change the steps
timerHandler.postDelayed(this, 100);

Timer not working in Android when calling invalidate()

I want to create a flashing effect by drawing a path with color grey, white (matching to the background), and then grey again. I want to flash 3 times, showing gray for 1 sec, white for 1 sec gray again for 1 sec, etc.
When I created a Handler for postDelayed(), the program skipped over the run() and did not execute it in the timing set, and no flashing occurred:
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
invalidate(); //calls onDraw()
Log.d(TAG, "Flashing now now");
}
}, 1000);
How would I implement such a flashing functionality with a timer and flash it 3 times?
Thanks!
You can try something like this,
int delay = 5000; // delay for 5 sec.
int period = 1000; // repeat every sec.
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
System.out.println("done}
}, delay, period);
Otherwise you have plenty other examples to follow like this Example 1, Example 2, Example 3 etc. Let me know if you still need help.
This is a working code example of how we flash a globe from blue to red and back again. You can modify the code on the inside to limit how many times and what time delay you want.
protected MyGlobeFlasherHandler handlerFlashGlobe = new MyGlobeFlasherHandler(this);
#Override
protected onCreate(Bundle bundle) {
handlerFlashGlobe.sendEmptyMessageDelayed(0, 700);
}
/**
* private static handler so there are no leaked activities.
*/
protected static class MyGlobeFlasherHandler extends Handler {
private final WeakReference<HomeBase> activity;
public MyGlobeFlasherHandler(HomeBase activity) {
this.activity = new WeakReference<HomeBase>(activity);
}
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (activity.get() != null) {
if (activity.get().shadedGlobe) {
activity.get().imgData.setImageDrawable(activity.get().getResources().getDrawable(R.drawable.globe_blue));
} else {
activity.get().imgData.setImageDrawable(activity.get().getResources().getDrawable(R.drawable.globe_red));
}
activity.get().shadedGlobe = !activity.get().shadedGlobe;
sendEmptyMessageDelayed(0, 700);
}
}
}

To set a Time period for enabling and disabling the button

I have set a counter and when the counter value is zero the button will be disabled. But I want to re-enable it after 3minutes. so, how am I supposed to set a timer for this?
One option is you could use a handler that will run in 3 minutes
Handler h = new Handler();
h.postDelayed(new Runnable() {
#Override
public void run() {
}
},3_MINUTES_IN_MILIS);
okay. So when you disable the button, you can do this,
button.postDelayed(new Runnable() {
#Override
public void run() {
//enable the button
}
}, 3 * 60 * 1000);
This code should be run from UI/Main thread
When you disable the button, call this code
Handler handler = new Handler();
handler .postDelayed(new Runnable() {
#Override
public void run() {
}
},3 * 60 * 1000);
Let me know if it solved your purpose.

Updating UI on button click after a certain time

I have a TextView. I want to update its text (append a "1") after 1 second of a button click.
public class HaikuDisplay extends Activity {
Method m;
Timer t;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
t = new Timer();
m = HaikuDisplay.class.getMethod("change");
}
//Event handler of the button
public void onRefresh(View view)
{
//To have the reference of this inside the TimerTask
final HaikuDisplay hd = this;
TimerTask task1 = new TimerTask(){
public void run(){
/*
* I tried to update the text here but since this is not the UI thread, it does not allow to do so.
*/
//Calls change() method
m.invoke(hd, (Object[])null);
}
};
t.schedule(task1, 1000);
}
public void change()
{
//Appends a "1" to the TextView
TextView t = (TextView)findViewById(R.id.textView1);
t.setText(t.getText() + "1");
}
//Event handler of another button which updates the text directly by appending "2".
//This works fine unless I click the first button.
public void onRefresh1(View view)
{
TextView t = (TextView)findViewById(R.id.textView1);
t.setText(t.getText() + "2");
}
}
Consider all Exceptions be handled.
On first click, m.invoke gives InvocationTargetException. But it calls the method change() on successive invokes without any Exceptions(verified by logging). But it does not update the text. Where am I wrong?
Also, I see in the debugger that it creates a new Thread every time I click the button. That is fine. But why isn't it removing the previous Threads though their execution has been completed?
Do something like this
public void onRefresh1(View v) {
// You can have this in a field not to find it every time
final EditText t = (EditText) findViewById(R.id.textView1);
t.postDelayed(new Runnable() {
#Override
public void run() {
t.append("1");
}
}, 1000);
}
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Update UI
}
}, 1000);
implement this on button click
UPDATE:
There are some other answers. dtmilano suggested another solution which is almost same to mine except he is calling the postDelayed method of View class and In my answer I used postDelayed method of handler class.
from the api reference of android the postDelayed method of Handler says
The runnable will be run on the thread to which this handler is
attached.
and the postDelayed method of View says
The runnable will be run on the user interface thread.
This is the only difference between these two solution. in my answer instead of creating new Handler every time you can use any other handler instance. Then the runnable will be run on that thread where that specific handler is declared. And if the postDelayed of EditText is used the the runnable method will be run on the user Interface Thread.
Now the performance issue, both has the same performance (If anybody can prove me wrong with reference I will be happy)
That's looking awful convoluted - have you considered using CountDownTimer instead?
new CountDownTimer(1000, 1000) {
public void onTick(long millisUntilFinished) {
// no-op
}
public void onFinish() {
change();
}
}.start();
This should call change (and hence change the text) on the UI thread, avoiding reflection and threading errors.
Hi Use the following code for that. Hope this will help you .
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
// your code here
}
},
1000
);
Have a look of this question also.
display data after every 10 seconds in Android
You can try with this also.
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
public void run() {
doStuff();
/*
* Now register it for running next time
*/
handler.postDelayed(this, 1000);
}
};
**EDIT 3**
Try with this once you are need to enable once (i mean if you put your code in yourmethod()== this will get automatically call 1 seconds once.
private Timer timer;
TimerTask refresher;
// Initialization code in onCreate or similar:
timer = new Timer();
refresher = new TimerTask() {
public void run() {
yourmethod();
};
};
// first event immediately, following after 1 seconds each
timer.scheduleAtFixedRate(refresher, 0,100);

Android - Run a thread repeatingly within a timer

First of all, I could not even chose the method to use, i'm reading for hours now and someone says use 'Handlers', someone says use 'Timer'. Here's what I try to achieve:
At preferences, theres a setting(checkbox) which to enable / disable the repeating job. As that checkbox is checked, the timer should start to work and the thread should be executed every x seconds. As checkbox is unchecked, timer should stop.
Here's my code:
Checking whether if checkbox is checked or not, if checked 'refreshAllServers' void will be executed which does the job with timer.
boolean CheckboxPreference = prefs.getBoolean("checkboxPref", true);
if(CheckboxPreference == true) {
Main main = new Main();
main.refreshAllServers("start");
} else {
Main main = new Main();
main.refreshAllServers("stop");
}
The refreshAllServers void that does the timer job:
public void refreshAllServers(String start) {
if(start == "start") {
// Start the timer which will repeatingly execute the thread
} else {
// stop the timer
}
And here's how I execute my thread: (Works well without timer)
Thread myThread = new MyThread(-5);
myThread.start();
What I tried?
I tried any example I could see from Google (handlers, timer) none of them worked, I managed to start the timer once but stoping it did not work.
The simpliest & understandable code I saw in my research was this:
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
// your code here
}
},
5000
);
Just simply use below snippet
private final Handler handler = new Handler();
private Runnable runnable = new Runnable() {
public void run() {
//
// Do the stuff
//
handler.postDelayed(this, 1000);
}
};
runnable.run();
To stop it use
handler.removeCallbacks(runnable);
Should do the trick.
Use a CountDownTimer. The way it works is it will call a method on each tick of the timer, and another method when the timer ends. At which point you can restart if needed. Also I think you should probably be kicking off AsyncTask rather than threads. Please don't try to manage your own threads in Android. Try as below. Its runs like a clock.
CountDownTimer myCountdownTimer = new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
// Kick off your AsyncTask here.
}
public void onFinish() {
mTextField.setText("done!");
// the 30 seconds is up now so do make any checks you need here.
}
}.start();
I would think to use AlarmManager http://developer.android.com/reference/android/app/AlarmManager.html
If checkbox is on call method where
AlarmManager alarmManager = (AlarmManager)SecureDocApplication.getContext()
.getSystemService(Context.ALARM_SERVICE);
PendingIntent myService = PendingIntent.getService(context, 0,
new Intent(context, MyService.class), 0);
long triggerAtTime = 1000;
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtTime, 5000 /* 5 sec*/,
myService);
If checkbox is off cancel alarm manager
alarmManager.cancel(myService);
"[ScheduledThreadPoolExecutor] class is preferable to Timer when multiple worker threads are needed, or when the additional flexibility or capabilities of ThreadPoolExecutor (which this class extends) are required."
per...
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html
It's not much more than the handler, but has the option of running exactly every so often (vice a delay after each computation completion).
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
...
final int THREAD_POOL_SIZE = 10;
final int START_DELAY = 0;
final int TIME_PERIOD = 5;
final TimeUnit TIME_UNIT = TimeUnit.SECONDS;
ScheduledThreadPoolExecutor pool;
Runnable myPeriodicThread = new Runnable() {
#Override
public void run() {
refreshAllServers();
}
};
public void startTimer(){
pool.scheduleAtFixedRate(myPeriodicThread,
START_DELAY,
TIME_PERIOD,
TIME_UNIT);
}
public void stopTimer(){
pool.shutdownNow();
}
Thanks to everyone, I fixed this issue with using Timer.
timer = new Timer();
timer.scheduleAtFixedRate(
new java.util.TimerTask() {
#Override
public void run() {
for (int i = 0; i < server_amount; i++) {
servers[i] = "Updating...";
handler.sendEmptyMessage(0);
new MyThread(i).start();
}
}
},
2000, 5000);

Categories

Resources