I'm creating a location tracking app for which I'm using the FusedLocationProviderClient.requestLocationUpdates()
method. One of its argument requires a Looper
object and I'm not completely sure how it works. Right now I'm just passing null
to it and it works as expected.
After researching I learned that the UI Thread is basically a HandlerThread
which already has its own Looper object (I know basic stuff but realized a bit late). So I used Looper.myLooper(
) instead of null this time and it still works.
private void getLocationUpdates(){
//checkSelfPermissions
fusedLocationProviderClient
.requestLocationUpdates(locationRequest,locationCallback,Looper.myLooper());
}
The documentation says that passing null
executes the locationCallback on the calling thread. So what happens when I use Looper.myLooper()
. Does both have the same effect when calling from literally any thread or am I missing something ?
Add a bit more info from the document:
Callbacks for LocationCallback will be made on the specified thread, which must already be a prepared looper thread.
Before you could understand the differences between passing null
and looper
as the value 3rd parameter, what you need to know are: looper
/ handler
/ HandlerThread
(because you mentioned HandlerThread, so I put it here together with the other two).
There are a lot of articles about these concepts, here are some just FYI:
Understanding Android Core: Looper, Handler, and HandlerThread
Explanation of handler, looper and related android thread classes
So I hope I can try to answer as simple as possible.
When you say:
I'm creating a location tracking app for which I'm using the FusedLocationProviderClient.requestLocationUpdates() method. One of its argument requires a Looper object and I'm not completely sure how it works. Right now I'm just passing null to it and it works as expected.
I assume you didn't start any new thread (e.g.: no new Thread()
or no new HandlerThread()
) at that time, if so, it's very possible you're calling the method getLocationUpdates()
in Android main thread, i.e.: the default thread, the one on which you can update the view (such as by default you can run textView.setText("xx")
without any problem, this is because you're in main thread - a.k.a. UI thread, by default), so passing null
will execute the callback on the calling thread
, i.e.: the main thread. And for now you need to know, main thread has a looper, we can call it main looper.
Then you say:
After researching I learned that the UI Thread is basically a HandlerThread which already has its own Looper object (I know basic stuff but realized a bit late). So I used Looper.myLooper() instead of null this time and it still works.
I assume you did something like below:
HandlerThread handlerThread = new HandlerThread();
getLocationUpdates();
private void getLocationUpdates(){
//checkSelfPermissions
fusedLocationProviderClient
.requestLocationUpdates(locationRequest,locationCallback,Looper.myLooper());
}
This time you passed Looper.myLooper()
as the 3rd param.
Yes by this way, you provided a looper
, and the locationCallback
will be executed on the specific thread of that looper
(talk a bit about looper later).
Because, however, it's very possible you're calling getLocationUpdates()
in the main thread again, so the Looper.myLooper
still returns the looper in main thread, yes you can consider the callback
is still running at main looper, the same as you set null
above.
However, if you change the code a bit like this:
HandlerThread handlerThread = new HandlerThread();
handlerThread.start();
getLocationUpdates(handlerThread.getLooper());
private void getLocationUpdates(Looper looper){
//checkSelfPermissions
fusedLocationProviderClient
.requestLocationUpdates(locationRequest, locationCallback, looper);
}
The callback
will be executed on the specified looper thread, i.e.: the looper object you get from handler.getLooper()
.
So what's difference on earth?
When you create a new HandlerThread
and start it, you're starting a new Thread
, and handlerThread.start()
will call the HandlerThread#run()
by default, at where the handler thread creates a new looper
, and this looper is prepared, tied with the handlerThread
you created just now.
You'll see the real difference if you are trying to update UI elements (such as update textview or mapview) in the callback. Because UI updating is allowed on UI thread only, if you set handlerThread.getLooper()
then you will be running into exception when trying to update UI; and there will be no problem if you set null
or Looper.myLooper()
from the main thread, reason for emphasis on main thread
is because, Looper.myLooper
will reference to different looper object when running on different threads.
Talk a bit about looper: With the looper
object, you can new a handler and pass the looper to it like: Handler handler = new Handler(looper)
, then when you call handler.post(new Runnable(...))
, the runnable will be executed on the looper thread which you set on the handler, I think that's what the API did behind the scenes.
Read more articles about handler
/ looper
/ HandlerThread
will be helpful I think.