I am trying to request a new location with FusedLocationProviderClient and Kotlin Coroutines. This is my current setup:
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()
}
}
}
But when trying to request a new location, I get the following exception:
java.lang.IllegalStateException: Can't create handler inside thread that has not called Looper.prepare()
However, if I would call Looper.prepare() (and Looper.quit() eventually) wouldn't it mean that I can call the function only once?
Any help is appreciated.
The problem is in the way you set up your coroutineContext
. Use this instead:
override val coroutineContext = Dispatchers.Main + job
If you ever need the IO
dispatcher, you can require it explicitly:
withContext(Dispatchers.IO) { ... blocking IO code ... }
To suspend the coroutine, call suspendCancellableCoroutine
, otherwise you won't get any benefit from structured concurrency.
Another detail, don't write any code after it.resume
in the suspendCancellableCoroutine
block. If the dispatcher chooses to resume the coroutine immediately, within the resume
call, that code won't execute until all the code of the coroutine has run (or at least until the next suspension point).
override fun onLocationResult(result: LocationResult) {
fusedLocationProviderClient.removeLocationUpdates(this)
it.resume(result.lastLocation.run { latitude to longitude })
}