What if I have handler.postDelayed thread already under execution and I need to cancel it?
I do this to cancel postDelays, per the Android: removeCallbacks removes any pending posts of Runnable r that are in the message queue.
handler.removeCallbacks(runnableRunner);
or use to remove all messages and callbacks
handler.removeCallbacksAndMessages(null);
If you don't want to keep a reference of the runnable, you could simply call:
handler.removeCallbacksAndMessages(null);
The official documentation says:
... If token is null, all callbacks and messages will be removed.
I had an instance where I needed to cancel a postDelayed while it was in the process of running, so the code above didn't work for my example. What I ended up doing was having a boolean check inside of my Runnable so when the code was executed after the given time it would either fire based on what the boolean value at the time was and that worked for me.
public static Runnable getCountRunnable() {
if (countRunnable == null) {
countRunnable = new Runnable() {
#Override
public void run() {
if (hasCountStopped)
getToast("The count has stopped");
}
};
}
return countRunnable;
}
FYI - in my activity destroy I do use code suggested by the other developers, handler.removecallback and handler = null; to cancel out the handle just to keep the code clean and make sure everything will be removed.
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.
I am creating an application that needs to update values every minute even if the app isn't running.
Of course, I have set up a simple Service to do that. I have debug messages set up to tell me when the Service starts, when it updates (every minute), and when it closes. I also have a message telling me when the values update inside a runOnUiThread() method. All of my messages activate except for the one in the runOnUiThread(). Is there something I'm doing wrong (of course there is)? What do I need to change?
Code:
#Override
public void handleMessage(Message message) {
try {
if (!serviceStarted) {
serviceStarted = true;
serviceTest = true;
while (serviceStarted) {
new MainActivity().runOnUiThread(new Runnable() {
public void run() {
OverviewFragment.refresh(getApplicationContext());
System.out.println("yay");
}
});
Thread.sleep(((1 /* minutes */) * 60 * 1000));
System.out.println("Updated values through service.");
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
stopSelf(message.arg1);
}
So there's no need to do that, unless you're creating a Thread inside
of it
Gabe Sechan's answer is correct.
But if you are using a separate thread then instead of following code:
new MainActivity().runOnUiThread(new Runnable() {
public void run() {
OverviewFragment.refresh(getApplicationContext());
System.out.println("yay");
}
});
Try, this code:
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
OverviewFragment.refresh(getApplicationContext());
System.out.println("yay");
}
});
As per Android docs
Caution: A service runs in the main thread of its hosting process—the
service does not create its own thread and does not run in a separate
process (unless you specify otherwise).
You can't create an Activity by calling new. It doesn't initialize properly that way.
Also, Services by default run on the UI thread. So there's no need to do that, unless you're creating a Thread inside of it. If you are- runOnUIThread is just syntactic sugar for posting a runnable to a handler. So you can just do that instead.
Try using a handler or LocalBroadcastManager to send a message to the activity.
See this question: Accessing UI thread handler from a service
You can use Looper.getMainLooper() within a Handler to post a Runnable that executes whatever you're trying to execute.
A good alternative though, like jinghong mentioned, is to use broadcasts - in other words, use a different pattern.
I've been writing android apps for some months now, and I'm at the point where I'm building an actual needed app.
As I want that to work nice and fast, I made a Workerthread to do all kinds of tasks in the background while the UI can...build up and work and stuff.
It's based on the Android Studio Drawer app blueprint.
In Main.onCreate I got my operator=new Operator(), which extends Thread.
Now, when loading a new Fragment, it sometimes calls MainActivity.operator.someMethod() (I made operator static so I can use it from anywhere), and after some time I realized, the only tasks actually running in background are those in the operators run() method and an Asynctask my login Fragment runs. Everything else the UI waits for to complete and therefore gets executed by the UI thread.
So I thought: no problem! My operator gets a handler which is built in run(), and I change those tasks:
public void run() {
Looper.prepare(); //Android crashed and said I had to call this
OpHandler = new Handler();
LoadLoginData();
[...Load up some Arrays with hardcoded stuff and compute for later use...]
}
public void LoadLoginData() {
OpHandler.post(LoadLoginDataRunnable);
}
private Runnable LoadLoginDataRunnable = new Runnable() {
#Override
public void run() {
if(sharedPreferences==null)
sharedPreferences= PreferenceManager.getDefaultSharedPreferences(context);
sessionID=sharedPreferences.getString("sessionID", null);
if(sessionID!=null) {
postenID = sharedPreferences.getString("postenID", PID_STANDARD);
postenName = sharedPreferences.getString("postenName", PID_STANDARD);
context.QuickToast(sessionID, postenName, postenID);
}
}
};
context is my MainActivity, I gave the operator a reference so I could send Toasts for Debugging.
But now, the Runnables seem to not run or complete, any Log.e or Log.d stuff doesn't arrive in the console.
After some googeling and stackoverflowing, everyone is just always explaining what the difference is between Handlers, Asynctask, and Threads. And the multitask examples always only show something like new Thread(new Runnable{run(task1)}).start times 3 with different tasks.
And so became my big question:
How to correctly, over a longer time (~lifecycle of the MainActivity), with different tasks, use a background thread?
Edit: to clarify, I would also like a direct solution to my special problem.
Edit 2: after reading nikis comment (thank you), the simple answer seems to be "use HandlerThread instead of thread". Will try that as soon as I get home.
Trying a HandlerThread now. It seems my OpHandler, initialized in run(), gets destroyed or something after run() has finished, not sure whats up here (this is btw another mystery of the kind I hoped would get answered here). I get a NullpointerException as soon as I try to use it after run() has finished.
Make your worker thread own a queue of tasks. In the run() method, just pop a task from the queue and execute it. If the queue is empty, wait for it to fill.
class Operator extends Thread
{
private Deque<Runnable> tasks;
private boolean hasToStop=false;
void run()
{
boolean stop=false;
while(!stop)
{
sychronized(this)
{
stop=hasToStop;
}
Runnable task=null;
synchronized(tasks)
{
if(!tasks.isEmpty())
task=tasks.poll();
}
if(task!=null)
task.run();
}
}
void addTask(Runnable task)
{
synchronized(tasks)
{
tasks.add(task);
}
}
public synchronized void stop()
{
hasToStop=true;
}
}
I have some code which sets a timer, but if a user sets the timer while it is in use I need to remove the runnable which runs the timer and start it again. But when no handler callback runnable exists and this code is called it crashes my application. So I need to check if a handler is running, if so then end it and restart it, but looking through the documentation and other Stackoverflow questions, I cannot see if this is possible.
Here is my code, I have commented around the code which should only be executed if a handler runnable exists:
submitTimer.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
String s = timer.getText().toString();
if(!s.equals(""))
{
//I NEED TO CHECK A RUNNABLE HANDLER EXISTS AND IF SO THEN RUN THIS CODE, IF NOT IGNORE THIS CODE
Map.handler.removeCallbacks(Map.getRunnable());
Map.runnable.run();
//I NEED TO CHECK A RUNNABLE HANDLER EXISTS AND IF SO THEN RUN THIS CODE, IF NOT IGNORE THIS CODE
int l = Integer.parseInt(s);
Map.timerinmins = l;
timer.setHint("Current Timer is "+Map.timerinmins);
timer.setText("");
Toast.makeText(Preferences.this, "Timer is set!", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(Preferences.this, "No value was entered", Toast.LENGTH_SHORT).show();
}
}
});
Can anyone help me figure out a way of checking the handlers current state?
Referring to Jay Snayder's reply:
Since there are cases in which Map.handler.hasMessages(CALLBACK_PRESENT_INTEGER) returns false even though Map.handler.sendEmptyMessage(CALLBACK_PRESENT_INTEGER) has been called (happened to me, too), it may be safer to use a boolean to determine whether the handler has callbacks or not.
So, the code logic is something very simple like this:
...
boolean callbackPresent = false;
...
if(!callbackPresent) {
// do what you have to do in case the handler doesn't have callbacks
// right before adding a callback to the handler, call:
callbackPresent = true;
} else {
// do what you have to do in case the handler has callbacks
// right before removing the callbacks from the handler, call:
callbackPresent = false;
}
...
I'm using this solution in an app and it's working perfectly.
If you want you could send an empty message when you first put a callback in and then check for that message in the handler. This empty message could represent that there is a callback present. Removing that message later could then be used similarly to see if the callback is still there. Don't have a relevant situation such as this to go from, but thought that I would at least try and share a possibility.
...
Map.handler.sendEmptyMessage(CALLBACK_PRESENT_INTEGER);
...
if(Map.handler.hasMessages(CALLBACK_PRESENT_INTEGER)
...
Map.handler.removeMessage(CALLBACK_PRESENT_INTEGER);
...
This is probably not ideal, but could be a potential solution if you have access to your handler from the point where your callback is used. Not sure if there is a direct way to find out.
Handler class has a method
boolean hasCallbacks(#NonNull Runnable runnable)
to check if there are any pending posts of messages with callback runnable in the message queue.
It exists since SDK 16 but it's hidden prior to SDK 29.
HandlerCompat class has the compatible version of this method for SDKs 16+:
boolean hasCallbacks(handler, runnable)
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.