在 FusedLocationProviderClient.requestLocationUpdates() 中使用 Looper.myLopper() 或 null 是一回事吗?

Is using Looper.myLopper() or null in FusedLocationProviderClient.requestLocationUpdates() the same thing?

我正在创建一个使用 FusedLocationProviderClient.requestLocationUpdates() 方法的位置跟踪应用程序。它的一个参数需要一个 Looper 对象,我不完全确定它是如何工作的。现在我只是将 null 传递给它,它按预期工作。

经过研究,我了解到 UI 线程基本上是一个 HandlerThread,它已经有自己的 Looper 对象(我知道基本的东西,但意识到有点晚)。所以这次我使用 Looper.myLooper() 而不是 null 并且它仍然有效。

private void getLocationUpdates(){
//checkSelfPermissions
fusedLocationProviderClient
.requestLocationUpdates(locationRequest,locationCallback,Looper.myLooper());
}

文档说传递 null 会在调用线程上执行 locationCallback。那么当我使用 Looper.myLooper() 时会发生什么?从字面意义上的任何线程调用时,两者是否具有相同的效果,还是我遗漏了什么?

如果您在主线程中调用 getLocationUpdates 方法,那么 locationCallback 也会在主线程中执行。 如果您在后台线程中调用 getLocationUpdates 并将 null 作为循环程序传递,您的回调将在后台线程中执行。 这取决于您的需要,您是否确实需要指定在哪个线程中执行回调。

document 添加更多信息: Callbacks for LocationCallback will be made on the specified thread, which must already be a prepared looper thread.

在理解传递 nulllooper 作为值 3rd 参数之间的区别之前,您需要了解的是:looper / handler / HandlerThread(因为你提到了HandlerThread,所以我把它和其他两个放在一起)。

关于这些概念的文章很多,这里仅供参考:


所以我希望我能尽量简单地回答。

当你说: 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.

我假设你当时没有启动任何新线程(例如:没有 new Thread() 或没有 new HandlerThread()),如果是这样,很可能你正在调用方法 getLocationUpdates() in Android main thread,即:默认线程,可以更新视图的线程(比如默认情况下你可以 运行 textView.setText("xx") 没有任何问题,这是因为你在主线程中 - a.k.a。UI 线程,默认情况下),所以传递 null 将在 calling thread 上执行回调,即:主线程。现在你需要知道,主线程有一个循环器,我们可以称之为主循环器。

然后你说: 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.

我假设你做了类似下面的事情:

HandlerThread handlerThread = new HandlerThread();
getLocationUpdates();

private void getLocationUpdates(){
//checkSelfPermissions
fusedLocationProviderClient
.requestLocationUpdates(locationRequest,locationCallback,Looper.myLooper());
}

这次您将 Looper.myLooper() 作为第 3 个参数。

是的,通过这种方式,你提供了一个looperlocationCallback将在那个looper的特定线程上执行(稍后再谈一下looper)。

但是,因为很有可能又在主线程调用getLocationUpdates(),所以Looper.myLooper还是returns主线程的looper,可以考虑callback 仍然 运行ning 在主循环器中,与上面设置的 null 相同。

但是,如果您像这样稍微更改代码:

HandlerThread handlerThread = new HandlerThread();
handlerThread.start();
getLocationUpdates(handlerThread.getLooper());

private void getLocationUpdates(Looper looper){
//checkSelfPermissions
fusedLocationProviderClient
.requestLocationUpdates(locationRequest, locationCallback, looper);
}

callback会在指定的looper线程上执行,即:你从handler.getLooper().

得到的looper对象

那到底有什么区别呢?

当您创建一个新的 HandlerThread 并启动它时,您将启动一个新的 Thread,而 handlerThread.start() 将默认调用 HandlerThread#run(),在处理程序线程创建一个新的 looper,并且此循环器已准备就绪,与您刚才创建的 handlerThread 绑定。

如果您尝试在回调中更新 UI 元素(例如更新 textview 或 mapview),您将看到真正的区别。因为 UI 更新仅允许在 UI 线程上进行,如果您设置 handlerThread.getLooper() 那么您将 运行 在尝试更新 UI 时进入异常;从主线程设置nullLooper.myLooper()都没有问题,之所以强调main thread是因为,Looper.myLooper 运行在不同线程上连接时将引用不同的循环对象。

谈谈循环器:使用 looper 对象,您可以新建一个处理程序并将循环器传递给它,例如:Handler handler = new Handler(looper),然后当您调用 handler.post(new Runnable(...)) 时, 运行nable 将在您在处理程序上设置的循环线程上执行,我认为这就是 API 在幕后所做的。

阅读更多关于 handler / looper / HandlerThread 的文章我认为会有帮助。