1
\$\begingroup\$

In my app I need to query location and scan wifi periodically. To conserve power I need to change the rate dynamically, depending on the distance to the target. Also I want to wait for both asynchronous calls to complete. Coroutines seemed the obvious choice, but since this is my first take at coroutines I want to have a second opinion.

I came up with the following solution:

@SuppressLint("MissingPermission")
suspend fun getCurrentLocation(context: Context): Location? = suspendCoroutine { cont ->
    val fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context)
    val locationRequest = LocationRequest()
    locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    locationRequest.numUpdates = 1
    val listener = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult?) {
            cont.resume(locationResult?.lastLocation)
        }
    }
    val task = fusedLocationProviderClient.requestLocationUpdates(locationRequest,
                                                                  listener,
                                                                  null)
    task.addOnFailureListener { cont.resumeWithException(it) }
}

also

class WifiScanner(val context: ContextWrapper) {
    val wifiManager: WifiManager

    init {
        wifiManager = context.baseContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
    }

    suspend fun getCurrentScanResult(): List<ScanResult> =
        suspendCoroutine { cont ->
            val wifiScanReceiver = object : BroadcastReceiver() {
                override fun onReceive(c: Context, intent: Intent) {
                    if (intent.action?.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) == true) {
                        val scanResults = wifiManager.scanResults
                        //sort so that strongest signal is on top
                        scanResults.sortByDescending { it.level }
                        Log.i("received wifi", scanResults.toString())
                        context.unregisterReceiver(this)
                        cont.resume(scanResults)
                    }
                }
            }
            context.registerReceiver(wifiScanReceiver,
                                     IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
            wifiManager.startScan()
        }
}

Both are executed concurrently like this:

val location = async { getCurrentLocation() }
val wifi = async { wifiScanner.getCurrentScanResult() }
val currentLocation = location.await()
val currentWifiInfo = wifi.await()

In a quick test it works fine, but it feels like abusing the system. Can I use this like that without breaking the common conventions?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.