FusedLocationProviderClient 与 Kotlin 协程

Ben*_* Ma 7 android kotlin fusedlocationproviderapi kotlin-coroutines

我正在尝试使用 FusedLocationProviderClient 和 Kotlin 协程请求一个新位置。这是我目前的设置:

class LocationProviderImpl(context: Context) : LocationProvider, CoroutineScope {

    private val TAG = this::class.java.simpleName

    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.IO

    private val fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context)
    private val locationRequest = LocationRequest().apply {
        numUpdates = 1
        priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    }

    override suspend fun getLocation(): LatLng = suspendCoroutine {
        val locationCallback = object : LocationCallback() {
            override fun onLocationResult(result: LocationResult) {
                result.lastLocation.run {
                    val latLng = latitude at longitude
                    it.resume(latLng)
                }

                fusedLocationProviderClient.removeLocationUpdates(this)
            }
        }

        try {
            fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
        } catch (e: SecurityException) {
            throw NoLocationPermissionException()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是在尝试请求新位置时,出现以下异常:

java.lang.IllegalStateException: Can't create handler inside thread that has not called Looper.prepare()
Run Code Online (Sandbox Code Playgroud)

但是,如果我调用 Looper.prepare()(最终调用 Looper.quit())是不是意味着我只能调用该函数一次?

任何帮助表示赞赏。

Mar*_*nik 3

问题在于您设置coroutineContext. 使用这个代替:

override val coroutineContext = Dispatchers.Main + job
Run Code Online (Sandbox Code Playgroud)

如果您需要IO调度程序,您可以明确要求它:

withContext(Dispatchers.IO) { ... blocking IO code ... }
Run Code Online (Sandbox Code Playgroud)

要挂起协程,请调用suspendCancellableCoroutine,否则您将无法从结构化并发中获得任何好处。

另一个细节是,不要it.resumesuspendCancellableCoroutine块后面编写任何代码。如果调度程序选择立即恢复协程,则在resume调用内,该代码将不会执行,直到协程的所有代码都运行完毕(或至少直到下一个暂停点)。

override fun onLocationResult(result: LocationResult) {
    fusedLocationProviderClient.removeLocationUpdates(this)
    it.resume(result.lastLocation.run { latitude to longitude })
}
Run Code Online (Sandbox Code Playgroud)