LocationManager.requestSingleUpdate and kotlin suspendCorout



我想使用LocationManager.requestSingleUpdate()获取设备的当前GPS位置。以下代码用于将对LocationManager的调用封装在kotlin挂起函数中:

private var locationManager =
context.getSystemService(LOCATION_SERVICE) as LocationManager
@RequiresPermission("android.permission.ACCESS_FINE_LOCATION")
suspend fun getCurrentLocationPreS(): Coordinate? = suspendCoroutine {
val handlerThread = HandlerThread("getCurrentLocation() HandlerThread")
handlerThread.start()
try {
// Use of deprecated function is ok because we are pre android S
locationManager.requestSingleUpdate(
LocationManager.GPS_PROVIDER,
{ location ->
handlerThread.quit()
it.resume(
Coordinate(
location.latitude,
location.longitude
)
)
},
handlerThread.looper
)
}
catch (ex: Exception) {
ex.printStackTrace()
it.resumeWithException(ex)
}
}

如您所见,我使用suspendCoroutine进行异步位置调用。这个实现适用于某些设备,但我在其他设备上有问题。有时,由于没有调用位置更新回调,supending函数永远不会返回并等待。该应用程序还具有所需的权限,并且已启用GPS。

什么样的边缘情况会导致函数永远不会返回的状态?

Logcat不指示任何异常或其他错误。该应用程序也不会崩溃。唯一的症状是getCurrentLocationPreS()永远不会返回。

启用GPS并不意味着它工作正常。当你在室内或高楼林立的地方时,你可能会有一个糟糕的信号。如果你查看requestSingleUpdate的实现,你会发现它使用了30s的超时,所以如果超时过期,你的回调将永远不会执行,你的协程将无限期地被卡住。

我建议您也使用超时进行此调用,或者考虑使用FusedLocationProviderClient,这样可以更安全地获得最后一个已知位置。

我还建议使用Looper.getMainLooper(),与确保正确管理HandlerThread相比,临时切换到主线程的运行时开销可以忽略不计

所以我对此的看法是这样的:

suspend fun getCurrentLocationPreS(): Coordinate? = withTimeoutOrNull(30.seconds){
suspendCoroutine { cont ->
try {
// Use of deprecated function is ok because we are pre android S
locationManager.requestSingleUpdate(
LocationManager.GPS_PROVIDER,
{ location ->
cont.resume(
Coordinate(
location.latitude,
location.longitude
)
)
},
Looper.getMainLooper()
)
}
catch (ex: Exception) {
ex.printStackTrace()
cont.resumeWithException(ex)
}
}
}

最新更新