I have a class with multiple functions, each function were containing a separate handler to execute a runnable. Recently I realized that we must remove all the callbacks on the onDestroy method to avoid memory leaks, so for that, I declared a class-level handler and used the same handler to post runnable in all the functions.
Now my confusion is, what happens if we use the same instance of Handler for multiple runnable compared to a separate handler instance for each runnable?
No matter how many handlers you have, all those handler will pos their messages to the same queue which is main thread queue by default, unless you attach the handler to different looper objects excplicitly. To illustrate in code:
// This handler will be attached to the main thread's looper,
// hence to its queue implicitly but however this constructor
// is deprecated and is not recommended.
Handler handlerMain = new Handler();
// This handler will be attached to the main thread's looper
// object explicitly and this is the recommended way by Google.
Handler handlerMain = new Handler(Looper.getMainLooper());
// Another handler will be attached to the main thread's looper object.
Handler handlerMain2 = new Handler(Looper.getMainLooper());
// This is a custom handler thread whis has a looper object same as the apllication's main (ui) thread. But this one will be a background thread.
HandlerThread mHandlerThread = new HandlerThread("MyHandlerThread");
mHandlerThread.start();
// Now this handler will post messages to the mHandlerThread's queue.
// Hence you cannot access directly to the UI elements from this thread,
// you must post the ui touching codes to the main thread's queue.
Handler handlerCustom = new Handler(mHandlerThread.getLooper());
You can attach many handlers to a looper, the handlers are not the problem. The problem is the code that they post. You can post a delayed code that will be executed in a future time, and that code touches some UI elements of a fragment or access context of a fragment. The problem comes when the fragment's view and context is destroyed before the posted code has been executed.
Related
It seems like just calling new Handler() associates the handler with the current thread’s looper without any thread-specific arguments being passed to the constructor.
How does it identify what the current thread is, though?
Why is calling new Handler() in specific methods (onLooperPrepared(), onCreate() ) mandatory?
How does it identify what the current thread is, though?
If you dive into the calling new Handler(), you'll see the source code, I'll show you :
The red rectangle shows you that you need a Looper when you new Handler.
The case mLooper==null only occurs when you call new Handler not in the UI thread, or you didn't call Looper.prepare() in your work-thread.
Also, you can associate the handler with the current thread’s looper obviously, refer to Define a handler on the UI thread, like handler = new Handler(Looper.getMainLooper()).
How does it identify what the current thread is, though?
By calling Thread.currentThread()static method
Why is calling new Handler() in specific methods (onLooperPrepared(), onCreate() ) mandatory?
It actually is not mandatory depending on the task you want your handler to do.
But in general Looper is required for Handler to process messages.
More: How to Use Thread, Looper and Handler in Android
I'm confused by Handler and HandlerThread classes usage. The reason I'm trying to use them is I want to utilize AudioRecord class and its setRecordPositionUpdateListener method (reference). The methos description says:
Use this method to receive AudioRecord events in the Handler associated with another thread than the one in which you created the AudioTrack instance.
And that's exactly what I want - to setup the AudioRecord in the main thread, but receive data in a working thread. I figure I need a HandlerThread so I've created and started one. I've also defined a callback method that implements AudioRecord.OnRecordPositionUpdateListener interface. I want this callback to be called from the worker HandlerThread. What I don't understand now is how to create the Handler to pass to setRecordPositionUpdateListener.
To associate Handler with a certain thread, you should create it by passing corresponding Looper in its constructor. So, if you already have a HandlerThread it can be done in the following way:
Looper looper = myHandlerThread.getLooper();
Handler handler = new Handler(looper);
And that's it, just use this handler in setRecordPositionUpdateListener method and callback will be executed at the worker thread. If you need more explanation on Handler, Looper and HandlerThread, you can take a look here.
Android doc says: "Each Handler instance is associated with a single thread.."
So, if I define the following handler inside onCreate() method:
myHandler= new Handler();
then myHandler will be associated with the main (UI) thread?
I would like to see an example where a handler is "associated" with a worker thread. If you have any, I would appreciate.
Thanks.
There are two questions here.
The answer to the first is "yes". If that line of code is in onCreate, then myHandler is associated with the UI thread
A Handler is cannot be associated with just any thread. It must be associated with a Looper. The very first paragraph of the documentation of the Looper gives an example.
This much is true: If you use the default constructor for Handler, then your Handler object will be associated with the current thread - whatever it is.
However, you will note that the Handler class has several other constructors. If you provide the Looper argument to the Handler constructor, then your handler will be associated with the thread that is associated with the Looper. (Note that Looper detects and holds onto the thread that it is constructed in.)
You can create a Thread with a Looper by instantiating a Looper in the thread's run() method. But Android has already done this for you in the HandlerThread class: HandlerThread class is a subclass of Thread and has a Looper - perhaps a better name for HandlerThread would have been LooperThread. In any case, HandlerThread class also has a method called getLooper(). So, you first create an instance of HandlerThread, and then use its getLooper() as an argument to your Handler constructor:
HandlerThread myLooperThread=new HandlerThread("LooperThread");
myLooperThread.start();
Handler myLooperHandler=new Handler (myLooperThread.getLooper(),
new MyLooperHandlerCallback());
myLooperHandler.sendEmptyMessage(50);
The above snippet can be executed in your UI thread, but will still associate myLooperHandler with the HandlerThread's thread. You can verify this, by logging thread id, as in:
//runs in HandlerThread's thread
public class MyHandlerCallback implements Callback {
#Override
public boolean handleMessage(Message msg) {
Log.i("CALLBACK", "Thread ID:"+android.os.Process.myTid());
Log.i("CALLBACK", "msg.what:"+msg.what);
return true;
}
}
Note that it is best to instantiate threads in retained worker fragments, so that they are accessible across Activity boundaries.
since several days, I tried to figure out what exactly happens if I execute code in
void function(){
//somePreExecutionCode
new Handler().post(new Runnable(){
#Override
public void run(){
//someCode
}
});
}
It seems like it isn't blocking the UI, so buttons, which calls function() doesn't stuck in the clicked position until someCode has finished.
But if somePreExecutionCode starts a progressBar, the progressBar is shown at exactly the same moment, when someCode has finished.
I know, there are AsyncTasks for, but is there any other possibility?
And whats the difference between
new Handler().post
and
View.post
?
When an Android application is created, system creates a main thread of execution. This thread is referred to as UI thread and all UI related operations happen on this thread in order to avoid synchronization issues.
A Looper instance is created on this thread, which has a MessageQueue data structure. The Looper will be in an infinite loop waiting to read the Message / Runnable instances posted on the MessageQueue. To add Message7 / Runnable to the MessageQueue, Handler is used.
When you create a Handler instance, it will be associated with the current thread of execution and the Looper instance created on that particular thread.
Hence when you post a message via a Handler, the Message is added to the MessageQueue, which will be read in FIFO order by Looper and will be delivered to the target.
new Handler().post() and View.post are bit different.
When you post Messages via View.post, you are guaranteed the Message will be posted on UI thread's MessageQueue, since it internally uses Handler instance created on UI Thread.
If you create Handler instance on UI thread and post the Message using it on any thread, Message will be posted to the UI thread's MessageQueue.
If you create Handler instance on a non-UI thread and post Messages using it, they will be posted on non-UI thread's MessageQueue.
Putting it simply, there are Looper threads, for example, UI thread. Such thread has its own Looper, which runs a message loop for the thread.
Such thread, typically, has a Handler, which processes its Looper's messages - overriding public void handleMessage(Message msg) or executing a Runnable, which was posted to it's looper's message queue.
When you're creating a Handler in the context of UI thread (like you did in your code), it gets associated with UI thread's looper, so your \\someCode runs on UI thread.
I guess, in your use case new Handler().post(Runnable) and View:post(Runnable) are mostly the same, as they both add a Runnable to the UI thread message queue.
But they are not the same.
View:post(Runnable) will add a Runnable to the UI thread looper's message queue;
Handler:post(Runnable) will add a Runnable to its associated thread looper's message queue
My explanation is pretty much intuitive, so correct me anyone if I am wrong.
According to the Android View's documentation:
The Runnable will be run on the user interface thread
According to the Android Handler's documentation:
The Runnable will be run on the thread to which this handler is attached
So, in the Handler's case, you can create it in any thread you want, it's a kind of anchor that will execute the Runnable you provide in the thread it was created in.
In the View.post, you will always execute the Runnable in the uI thread.
There's something weird happening. I can't show all the code, but situation is like this;
Runnable program = new Runnable() {
#Override
public void run() {
//This code is running
new Handler();
//This code not running
}};
new Thread(program).start();
Log shows nothing. Main thread is working good.
You should always declare Handler in UI thread.You need to provide the Handler with a Looper from some thread.E.g. from main UI thread:
Handler mHandler = new Handler(Looper.getMainLooper());
Handler always runs in the Looper thread context. When you create another thread its context is different from the Looper. Right solution is to declare Handlers always in onCreate(), onStart() and onResume().
Creating a Handler needs a Looper polling on that thread first. By the way, there's usually no need to create a Handler inside non UI threads. Create a Handler outside the runnable. An easier alternative is to use runOnUiThread() method.