When is a Handler associated to a looper? - android

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

Related

Does declaring multiple Handler instance create multiple queue?

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.

how to get the Handler of the main ui thread?

using SurfaceTexture i need to call this function :
setOnFrameAvailableListener (SurfaceTexture.OnFrameAvailableListener listener,
Handler handler)
I would like to gave the Handler of the main UI thread, but I don't know how to get it ...
NOTE: i know i can do Handler handler = new Handler(Looper.getMainLooper()); but their is not an already created object of Handler accessible somewhere ?
HandlerThread handlerThread = new HandlerThread("BGHandler") // string here can be any name
handlerThread.start()
surfaceTexture.setOnFrameAvailableListener(this, Handler(handlerThread.getLooper())) //this means that the class calling this function implements the interface, I am sure u understand that since you have not asked for it, but if necessary I can elaborate more, just ask in the comments

HandlerThread and Handler: how to use AudioRecord.setRecordPositionUpdateListener?

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 handler association

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.

Create Handler in a class without get error "Looper not prepared"?

In my game, there is an game object class that need to use Handler to post a delay Runnable. However, everything I try to create an Handler in the object class, I receive error message:
Can't create handler inside thread
that has not called Looper.prepare()
I've Googled some solution, but all of them use another solution, not Handler. Is there any solution to use Handler in a normal class? Or any solution to run a Runnable after a determined delay?
I can't use sleep, because it paused all my game!
Thank you.
You are probably creating the Handler from a non-UI thread. Either (1) you attach your handler explicitly to the UI thread by passing the UI thread's looper to Handlers constructor, which means that messages posted to the Handler are also executed on the UI thread, or (2) you create a new Looper for the non-UI-thread: see here.
edit regarding (1): you would have to somehow pass the UI thread's looper to the "game object", for example when it is created. You can get a reference to UI's looper by calling getMainLooper() on a context (e.g. from an activity).
An alternative would be to create the handler in the activity and just pass the handler to your game object.

Categories

Resources