Safely post last task to Handler before quitting its thread - android

I've got a background Handler and associated HandlerThread.
val handlerThread = HandlerThread("my background thread").apply { start() }
val handler= Handler(handlerThread)
When exiting the lifecycle of this class, I want to clean up something in the background thread. So I post a runnable to clean it up, and then call handlerThread.quitSafely():
fun onDestroy() {
handler.post {
someObject.release()
}
handlerThread.quitSafely()
}
But I get the LogCat warning "sending message to a Handler on a dead thread", and someObject.release() is never called.
quitSafely() is supposed to stop the thread only after all messages have been processed, but not delayed messages. Looking into the source code, I see that handler.post posts the runnable with a delay of 0, so I guess that means it's not yet in the queue and quitSafely() won't wait for it to post?
What's the correct way to run one more clean-up runnable in the background thread before it's released? Could it be as simple as wrapping handlerThread.quitSafely() in a runnable and posting it to the main handler at the end of the background cleanup runnable?

This is what I ended up doing, and it seems to work fine.
fun onDestroy() {
handler.post {
someObject.release()
handlerThread.quitSafely()
}
}

Related

What are Handlers and Runnables for in Android?

Please I am blocked with this concept of Handlers and Runnables in Android. Can someone please give me detailed explanation on Handlers and Runnables? Their syntax and implementation? I have read many articles on this but the concepts are not still clear and are even deployed in Java. Thanks in advance
I'm going to try to simplify so bear with me if it is not 100% accurate.
Basically, a Handler is used to communicate with a MessageQueue associated with a Thread.
If you're on the main thread, or if you've called Looper.prepare() in the Thread that you're in, it has a Looper which is basically a holder for the MessageQueue.
This queue is constantly polled so that whenever a Message goes into it, it's dealt with on the Thread associated with this MessageQueue
If you're trying to execute a piece of code on a particular Thread, you have to use a Runnable. It is just an interface that has a void run() method which will be executed by the Looper, on its Thread.
Let's say you're doing a network request, you want it to happen on another Thread but when you get the result you somehow need to pass the data back to the Main Thread in order to update your UI because Views can't be modified from another Thread.
You would do it like so:
// This will let you run method on main thread (even if you're not on main thread)
private final Handler handler = new Handler(Looper.getMainLooper());
// This will let you run method on background thread
private final Executor executor = Executors.newSingleThreadExecutor();
public void doSomething() {
// posting to executor will go to background thread
executor.post(new Runnable() {
#Override
public void run() {
// This will now run on background thread
// you can for example do network request here
// posting to handler will go back to main thread
handler.post(new Runnable() {
#Override
public void run() {
// This will execute on the Main Thread
}
});
}
});
}

ExecutorService can not reuse its thread

I have the following task:
void task()
{
Looper.preapre();
handler = new Handler();
runnable = new Runnable()
{
if(notDone)
{
doSth();
handler.postDelay(runnable,timeInterval);
}
else
{
handler.removeCallBacks(runnable);
returnResult(); / the task is done and return some results
}
}
handler.post(runnable);
Looper.loop();
}
I use ExecutorService with single worker thread to run this task, the 1st time it runs just fine but the 2nd time the task is never be executed because the worker thread is still running the 1st task although i call removeCallBacks. Does anyone know where the problem is? Thanks in advance.
PS: I have try Looper.quit() but it kills the thread so the executor can not run the task on the dead thread.
After doing a research, i found that when a looper is attached to a thread, it will keep the thread active all the time even the thread is idle, so the ExecutorService can not get back the idle thread

Concurrency issue with Android Looper / Handler

I have a queue of Jobs which execute sequentially - the onRun() of each job looks like this:
#Override
public void onRun() throws Throwable {
if (Looper.myLooper() == null) {
Looper.prepare();
}
this.looper = Looper.myLooper();
makeQuery(looper);
Looper.loop();
}
the makeQuery() function does a database call that runs on a background thread that reports back to this job by posting a Runnable to a handler for this thread. Like so:
{
// ... make a query in another thread...
Handler handler = new Handler(looper);
handler.post(new Runnable() {...});
}
And when the result of the query is received, it does something then calls:
this.looper.quit();
So far so good - the first job on the queue executes fine, but the 2nd job gets as far as executing the query and then the code above that tries to post the result - handler.post - fails with this exception:
"sending message to a Handler on a dead thread"
One thing I noticed is that the 2nd job is running on the same thread as the first, so the JobManager is reusing it, given that it has been configured for FIFO operation, I guess that is efficient. However, why is the thread in the state 'Quitting' when the second job runs on it that is causing this process to barf?

How to clear UI Thread queue

I use runOnUIThread method to pass Runnable tasks to the main thread queue, but I need to clear all queue tasks, that I sent before, before sending a new one. How to do it?
use an Handler to post. It has the same effect of runOnUiThread. On your handler instance you can call removeCallbacks(null), which will remove every element in the Handler queue, or removeCallbacks(yourannableinstance) which remove every element of yourannableinstance kind
You can use the removeCallbacks(Runnable r) method. If they are anonymous then you can use removeCallbacksAndMessages(null);. If this doesn't fix the problem please give me more details
The UI thread is also a Looper thread, and it only have one Message Queue.
So if you create a handler in UI thread, and then call handler.post(runnable), the runnable task will store in the message queue.
If you call runOnUIThread(), the run task will also store in the same message queue.
5289 public final void runOnUiThread(Runnable action) {
5290 if (Thread.currentThread() != mUiThread) {
5291 mHandler.post(action); // runOnUiThread also calls handler.post()
5292 } else {
5293 action.run();
5294 }
5295 }
And mHandler.removeCallbacksAndMessages(null) would help you to remove all callbacks and messages.

Remove callback for handler not work

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.

Categories

Resources