I use a Handler, which post some Runnable via postDelayed(r, DELAY_TIME), but I need to execute all Runnables before posting new via postDelayed.
Any good ideas how to achieve this as simple as possible?
Edit:
I want it basically like this:
Runnable r = new Runnable() {
// Do some fancy action
};
if (mHandler.runnablesScheduled) {
mHandler.flushAllScheduledRunnables();
}
mHandler.postDelayed(r, DELAY);
In an Array, Keep track of runnables that are going to be called, then, if you want them to be called, cancel the postDelayed and call the runnables directly, to fire them just call the run() method from the runnable. Example code:
// Declaring the Handler and the Array that is going to track Runnables going to be tracked.
final mHandler = new Handler();
final List<Runnable> callStack = new ArrayList<Runnable>();
// Method to remove a runnable from the track Array.
public void removePostDelayed(Runnable run) {
callStack.remove(run);
}
// Method that we use in exchange of mHandler.postDelayed()
public void myPostDelayed(Runnable run, int delay) {
// I remove callbacks because I don't know if can be called 2 times.
mHandler.removeCallbacks(run);
// We remove the Runnable from the tracking Array just in case we are going to add a Runnable that has not been called yet.
removePostDelayed(run);
// We add the runnable to the tracking Array and then use postDelayed()
callStack.add(run);
mHandler.postDelayed(run, delay);
}
// This is the Runnable. IMPORTANT: Remember to remove the Runnable from the tracking Array when the Runnable has been called.
Runnable myRunnable = new Runnable() {
#Override
public void run() {
// Do some fancy stuff and remove from the tracking Array.
removePostDelayed(this);
}
}
// Method to execute all Runnables
public void callAllStack() {
// We create a copy of the tracking Array because if you modify the Array while you are iterating through it, will return an Exception.
List<Runnable> callStackCopy = new ArrayList<Runnable>();
// here we copy the array and remove all callbacks, so they are not called by the Handler.
for (Runnable runnable : callStack) {
callStackCopy.add(runnable);
mHandler.removeCallbacks(runnable);
}
// Then we call all the Runnables from the second Array
for (Runnable runnable : callStackCopy) {
runnable.run();
}
// And clear the tracking Array because the Handler has no more Runnables to call (This is redundant because supposedly each run() call removes himself from the tracking Array, but well... just in case we forgot something).
callStack.clear();
}
// Example of postDelaying a Runnable while tracking if has been fired.
myPostDelayed(myRunnable, 1000)
// Example of firing all Runnables.
callAllStack();
Is pretty easy and I've commented it so you can understand it better, but if you don't understand something, just comment it. You can modify it to support multiple calls to the same Runnable or just creating your own class extension of a Handler called TrackingHandler with these functions implemented or something.
I've written the code right now on the fly so is possible that is plenty of typos, don't know.
Well, all the Runnables are run on a queue in your handler, so if you want to run something at the end of it, the easiest way that comes to mind is to place it as another Runnable on the queue:
mHandler.post(new Runnable() {
#Override
public void run() {
mHandler.postDelayed(r, DELAY);
}
});
You need a few things to make this work.
Use Handler.sendMessageDelayed(Message) instead of Handler.postDelayed and assign a meaningful what value to your Message
When you need to flush the queue, check whether anything is queued already with Handler.hasMessages(int). If there's anything, you can remove it with Handler.removeMessages and execute it yourself.
Related
I want perform a network call in every 30sec to push some metrics to Server. Currently I am doing it using thread.sleep(). I found some articles saying thread.sleep() has some drawbacks. I need to know am I doing it right? or Replacing the thread with Handler will improve my code?
public static void startSending(final Context con) {
if (running) return;
running = true;
threadToSendUXMetrics = new Thread(new Runnable() {
#Override
public void run() {
do {
try {
Thread.sleep(AugmedixConstants.glassLogsPushInterval);
} catch (InterruptedException e) {
mLogger.error(interrupt_exception + e.getMessage());
}
// to do to send each time, should have some sleep code
if (AugmedixConstants.WEBAPP_URL.equals(AugmedixConstants.EMPTY_STRING)||!StatsNetworkChecker.checkIsConnected(con)) {
Utility.populateNetworkStat();
mLogger.error(may_be_provider_not_login_yet);
} else
sendUXMetrics();
} while (running);
if (!uxMetricsQueue.isEmpty()) sendUXMetrics();
}
});
threadToSendUXMetrics.start();
}
If You are using only one thread in the network, then usage of the thread.sleep() is fine. If there are multiple threads in synchronization, then the thread.sleep() command will block all the other threads that are currently running.
As per the details you've provided, there is only one thread present which isn't blocking any other active threads which are running in synchronization, so using thread.sleep() shouldn't be a problem.
Use Handler.postDelayed to schedule tasks if you are working in UI Thread and Thread.sleep if you are working in background thread.
Apparently you are sending some data using network, you must do it in the background thread, hence Thread.sleep is recommended.
Simple is:
Thread.sleep(millisSeconds): With this method, you only can call in background tasks, for example in AsyncTask::doInBackground(), you can call to delay actions after that. RECOMMENDED CALL THIS METHOD IN BACKGROUND THREAD.
Handler().postDelayed({METHOD}, millisSeconds): With this instance, METHOD will trigged after millisSeconds declared.
But, to easy handle life cycle of Handler(), you need to declare a Handler() instance, with a Runnable instance. For example, when your Activity has paused or you just no need call that method again, you can remove callback from Handler(). Below is example:
public class MainActivity extends Activity {
private Handler mHandler = Handler();
public void onStart(...) {
super.onStart(...)
this.mHandler.postDelayed(this.foo, 1000)
}
public void onPaused(...) {
this.mHandler.removeCallback(this.foo)
super.onPaused(...)
}
private Runnable foo = new Runnable() {
#Override
public void run() {
// your code will call after 1 second when activity start
// end remove callback when activity paused
// continue call...
mHandler.postDelayed(foo, 1000)
}
}
}
The code above just for reference, I type by hand because don't have IDE to write then copy paste.
Is it possible to call Runnable without execute-ing it?
AsyncTask.execute(new Runnable() {
#Override
public void run() {
//TODO your background code
}
});
In iOS a closure can be executed, or just called like a method. In first case there is a small delay, other code chunk can come first to the loop, in second case right at the time when call is done code is Runnable is performed. How is it with Android?
Runnable runnable = new Runnable() {...}
runnable.run();
and if you need to be able to decide where to execute it depending on your current thread: UI or non-UI, you can do something like this
if (Looper.getMainLooper().isCurrentThread()) { ... } else { ... }
Some useful information can be found here Runnable
I think this is a beginner (me) question, then for you guys is easy to answer.
I have this method:
public void onQuantityTextChange(final int value)
{
new Thread(new Runnable() {
public void run() {
addProductToCart(value);
view.post(new Runnable() {
public void run() {
updateTotals();
}
});
}
}).start();
}
My question is: this peace of code:
view.post(new Runnable() {
public void run() {
updateTotals();
}
is executed only when this addProductToCart(value); method is executed(finished)? or is it more safe to use AsyncTasks with doInBackground() and onPostExecute()?
It is always executed after: addProductToCart(value);
But if that function starts a Thread or AsyncThread or similar then the function will return before that task finishes.
To summarize: nobody can answer without the contents of addProductToCart
That largely depends on whether or not your method addProductToCart(value) starts another thread of its own. If it does, then there's no guarantee as the thread will start and finish as the system sees fit. If not, then you will not call view.post(...) until that thread is complete.
Another thing to watch out for depending on what you're trying to accomplish is the method inside view.post(...) is not guaranteed to run immediately. What this method does is put Runnable objects inside a message queue. This means, this runnable won't execute until the other elements in the message queue execute first. Secondly, the message queue can run at any time meaning even if this is the first Runnable in the queue it will start eventually, but not necessarily immediately.
I am basically doing the code post at the bottom of this message.
foobar() posts an event into a common state machine.
I also have touchscreen events posted into the common state machine.
Is it true, that by using a handler I have no synchronization issues?
(i.e., my state machine won't be message up by a touch even and a foobar event) at the same time?
private Handler handler = new Handler();
handler.postDelayed(runnable, 100);
private Runnable runnable = new Runnable() {
#Override
public void run() {
/* do what you need to do */
foobar();
/* and here comes the "trick" */
handler.postDelayed(this, 100);
}
};
The same instance of Handler object will process through the queue of messages/runnables passed to it on the Looper of choice (main thread by default).
So NO, if you send a list of messages to the Handler they will be run through 1 at a time never in parallel.
BUT if you are concerned about synchronization issues you should synchronize(object) {} your code inside your methods around a common object, that way they will wait for lock on that common object meaning that you could call that method from anywhere and it would never run in parallel with any other code using synchronize(object) {}.
I've been playing about with Runnables and have discovered that if you postDelayed a Runnable on a View then removing the callback won't work, however if you do the same but post the Runnable on a Handler then removing the callback does work.
Why does this work (Runnable run() code never gets executed):
Runnable runnable = new Runnable() {
#Override
public void run() {
// execute some code
}
};
Handler handler = new Handler();
handler.postDelayed(runnable, 10000);
handler.removeCallbacks(runnable);
where as this doesn't (Runnable run() code always gets executed)?:
Runnable runnable = new Runnable() {
#Override
public void run() {
// execute some code
}
};
View view = findViewById(R.id.some_view);
view.postDelayed(runnable, 10000);
view.removeCallbacks(runnable);
If the View is not attached to a window, I can see this happening, courtesy of what looks like a bug in Android. Tactically, therefore, it may be a question of timing, making sure that you do not post or remove the Runnable until after the View is attached to the window.
If you happen to have a sample project lying around that replicates this problem, I'd like to take a look at it. Otherwise, I will try making my own, so I can have something I can use to report my presumed bug.
UPDATE
As mentioned in the comments, removeCallbacks() on more ordinary widgets works, so it appears this is a WebView-specific problem, per the OP's sample code.
For various reasons, the View's handler (view.getHandler()) may not be ready when you want to initiate the animation.
Therefor you should probably wait before assigning the runnable to the view.
Assuming you are trying to do that from within an Activity, here is a code that waits for the handler to be available before posting the runnable:
private void assignRunnable(final View view, final Runnable runnable, final int delay)
{
if (view.getHandler() == null) {
// View is not ready, postpone assignment
this.getView().postDelayed(new Runnable() {
#Override
public void run() {
assignRunnable(view, runnable, delay);
}
}, 100);
//Abort
return;
}
//View is ready, assign the runnable
view.postDelayed(runnable, delay);
}
Looking at ViewRootImpl.java, the semantics of View.removeCallbacks() seem unclear to say the least.
RunQueue.removeCallbacks just removes the Runnables from an ArrayList. See here.
If RunQueue.executeActions is called before removeCallbacks, then the ArrayList is cleared in all cases making removeCallbacks a no-op. See here.
RunQueue.executeActions is called for every traversal.... See here.
So unless I miss something, View.removeCallbacks will not work if a traversal has happened since you called View.post.
I'll stick to #james-wald comment above and not use View.post