I have two suspend functions which are callbackFlow. I call one of them from suspend function which return String. I want to wait for location from getLocation() in serializeEvent() function and after getting value return string.
suspend fun getLocation(applicationContext: Context) = callbackFlow {
val locationProvider = LocationServices.getFusedLocationProviderClient(applicationContext)
if (isLocationPermissionGranted(applicationContext)) {
locationProvider.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, null)
.addOnSuccessListener {
if (it != null) {
trySend(it)
} else {
launch { trySend(getLastKnownLocation(locationProvider)) }
}
}
}
}
private suspend fun getLastKnownLocation(locationProvider: FusedLocationProviderClient) = callbackFlow {
runCatching {
locationProvider.lastLocation
.addOnSuccessListener { trySend(it) }
.addOnCanceledListener { trySend(null) }
.addOnFailureListener { trySend(null) }
}.onFailure {
trySend(null)
}
awaitClose { this.cancel() }
}
How I can return string when I get value from getLastKnownLocation()
suspend fun serializeEvent(eventJson: JSONObject): String {
CoroutineScope(Dispatchers.Default).launch {
LocationProvider.getLocation(getApplicationContext!!).collect {
}
}
//some code here
return eventJson.toString()
}
CodePudding user response:
It doesn't make sense to turn a Google Task into a Flow using callbackFlow
, because a Task only produces one thing, not a series of things. Typically, the way to convert for coroutines a callback that returns only once is to convert it into a suspend function using suspendCoroutine
or suspendCancellableCoroutine
.
However, a Task.await()
extension suspend function is already provided so you can use it synchronously with coroutines. (Make sure you're using the -ktx
version of the location library.) Instead of using listeners, you surround it with try/catch.
It seems like you just want to return null if no location is available, so here's how I would rewrite that function:
suspend fun getLocationOrNull(applicationContext: Context): Location? {
if (!isLocationPermissionGranted(applicationContext)) {
return null
}
val locationProvider = LocationServices.getFusedLocationProviderClient(applicationContext)
return try {
locationProvider.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, null)
.await() ?: locationProvider.lastLocation.await()
} catch (e: Exception) {
Log.w(TAG, "Failed to retrieve current location")
null
}
}
In your usage site code, it doesn't make sense that you're launching a coroutine from within the suspend function. If you want to do something and wait for it in a suspend function, you can just do it directly without firing off a new coroutine in some other CoroutineScope.
suspend fun serializeEvent(eventJson: JSONObject): String {
val knownLocation: Location? =
LocationProvider.getLocation(EuvicMobileSDK.getApplicationContext!!)
//some code here. Do something with knownLocation, which might be null
return eventJson.toString()
}
Side note, it looks like a code smell to me that you have to use !!
when getting your application context. An Application Context must always exist, so it shouldn't need to be a nullable property.
CodePudding user response:
.addOnSuccessListener { trySend(it) }