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
Related
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()
}
}
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
}
});
}
});
}
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?
Im unsure if my service is running on a own thread.
Im doing this in the onCreate method in my service:
#Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ServiceStartArguments");
thread.start();
}
I can see the thread being created when debugging but still I'm unsure as I see people go for something like this:
handler=new Handler();
Runnable r=new Runnable()
{
public void run()
{
tv.append("Hello World");
}
};
handler.postDelayed(r, 1000);
How, what why? Please explain, I don't get it!
This makes me confused also..
I/Choreographer﹕ Skipped 52 frames! The application may be doing too much work on its main thread.
Anything that's executed in the run() method of HandlerThread is running on the thread you are starting with thread.start() in onCreate(). Anything else in the Service is running on the UI thread by default (unless you are using IntentService, then onHandleIntent() is automatically running in a non-UI thread).
The pattern you posted is probably demonstrating how to update the UI on the UI thread from a different thread.
Use this to check for thread's id:
long myThreadId = Thread.currentThread().getId();
Log.d("thread debuging", "Thread id is: " + myThreadId);
Alternatively you can also use these to set a name for your threads:
Thread.currentThread().setName("A_NAME_FOR_THIS_THREAD");
String threadName = Thread.currentThread().getName();
On iOS, if I want my current thread of execution to wait (ie. block) and the main loop to run so that the thread of execution next in the main queue can execute, I invoke:
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
How would I go about doing the equivalent on Android?
This is indeed possible to do in Android. Shachar's answer is on the right track. The problem is not that the main loop will block (unless the code was executed on the main thread, but that's not what the question is proposing). The problem is that the other thread doesn't block, but is simply looping and burning CPU cycles in the while loop. Here is a blocking run on main method I use in my app:
/**
* Runs the runnable on the main UI thread. If called from a thread other than the UI thread,
* this method will block the calling thread and return only after the runnable has completed
* execution on the main UI thread.
* #param runnable Runnable to run on the main UI thread
*/
public static void blockingRunOnMain(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) { // Already on UI thread run immediately
runnable.run();
}
else { // Queue to run on UI thread
final MainRunMonitor lock = new MainRunMonitor();
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(runnable);
// Task to notify calling thread when runnable complete
mainHandler.post(new Runnable() {
#Override
public void run() {
synchronized (lock) {
lock.mRunComplete = true;
lock.notify();
}
}
});
// Block calling thread until runnable completed on UI thread
boolean interrupted = false;
try {
synchronized (lock) {
while (!lock.mRunComplete) {
try {
lock.wait();
} catch (InterruptedException e) {
// Received interrupt signal, but still haven't been notified, continue waiting
interrupted = true;
}
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt(); // Restore interrupt to be used higher on call stack (we're not using it to interrupt this task)
}
}
}
}
MainRunMonitor is a simple class, in my case a private inner class to the class that implements blockingRunOnMain():
/**
* Monitor to lock calling thread while code is executed on UI thread.
*/
private static class MainRunMonitor {
private boolean mRunComplete = false;
}
blockingRunOnMain() is used by passing it a Runnable to run on the main thread:
blockingRunOnMain(new Runnable() {
#Override
public void run() {
workToDoSynchronouslyOnMain();
}
});
The first part of the blockingRunOnMain() method checks if the method is being called from the main thread and if so, simply executes the code immediately. Since the function of blockingRunOnMain() is to synchronously run the Runnable code before the method returns, this will have this same result even if called from the main thread itself.
If the method is called from a thread other than the main thread, we then post the Runnable to a Handler which is bound to the main thread's Looper. After posting the Runnable parameter, we then post another Runnable that will execute after the Runnable parameter completes execution, since the Handler executes posted Messages and Runnables in order. This second Runnable serves to notify the blocked thread that the work has been completed on the main thread.
After posting the second Runnable we now block the background thread and wait until we're notified. It's important to synchronize the operations performed on lock so that the operations are atomic on each thread.
The background thread calls wait() on the monitor and waits until mRunComplete == true. If it gets an InterruptedException, it's important to continue waiting and restore the interrupted state of the thread after we're done, since we're not using the interrupt mechanism ourselves to cancel our task, restoring it allows another method higher on the call stack to handle the interrupt. See "Dealing with InterruptedException".
When the Runnable parameter has completed execution and the second posted Runnable executes, it simply sets mRunComplete to true and notifies the blocked thread to continue execution, which finding mRunComplete == true now returns from blockingRunOnMain(), having executed the Runnable parameter synchronously on the main UI thread.
One short workaround is to have a boolean that is changed by the next main thread loop.
running on main thread can be done with runOnUIthread (or getting the main looper yourself)
moving to the next loop can b easely done with handler.postDelayed(Runnable run, long delayMills), and a no-time delay.
so you could do this:
nextMainLoopDone = false;//This should be changed to a thread safe boolean, could use AtomicBoolean
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
nextMainLoopDone = true;
}
}, 1/* delay for no time, just to next loop*/);
while(!nextMainLoopDone) {
;
}
I'm sorry to disappoint you, but it is not possible to do what you're asking for in Android.