NSDManager and co-routine

from the CommonsWare Community archives

At April 28, 2021, 12:06pm, Jan asked:

NetworkServiceManager (NSDManager) was only working about 10% of the time until I discovered
that it works if called from a coroutine.
So I have this code:

fun startDiscovery(uuid:String) {
    val scope = CoroutineScope(Job() + Dispatchers.Default)
    val job = scope.launch {
        val discoveryJob = async { findDevice(uuid) }
        val result = discoveryJob.await()
        // immediate return
        delay(2000)
        // after the delay, I have url to device IF it was found in discovery
        --->>>> should I put the delay in another job and cancel that job in an observable???
    }
    
fun findDevice(uuid:String)  {
// NsdHelper is the usual NSD Manager helper class that has the callbacks such as onServiceResolved
    val nsdHelper = NsdHelper()
    // pass a lambda to be called from onServiceResolved 
    nsdHelper.findThisDevice(uuid,onFindDevice = { device ->
        urlFullAddress = device.urlFullAddress
        // urlFullAddress is what the caller needs but if device isn't found
        // then it's never set so discovery service needs to be stopped at some point (probably after the time delay??)
    })
}

In your co-routines book, you say:

“Conversely, the only reason to use async() over launch() is to hold onto the
Deferred that async() returns, so you can get the result of the coroutine — we will
explore this more in the next chapter.”

But the next chapter hasn’t been written yet.

I don’t know if that would help because my callback is being called from a callback
so how would I return that in lambda??? – (result = discoveryJob.await())

First problem: how to immediately know if I get a result - without waiting the full delay(2000).
Should I use an observable that then would cancel the delay (if put in a job that could be cancelled)?

Keep in mind, I may not get any result (no device on network). (I need to add call to stopDiscovery after the delay(2000) line).

The above code is not in a ViewModel but I have similar code that is in a ViewModel (in case it matters).

Is there a better way to my approach? I’m just excited I can finally get NSD to consistently discover! It used to never give me the onServiceFound callback.


At April 28, 2021, 4:29pm, Jan replied:

Darn! It is not fixed. I had bluetooth turned off. As soon as I turned bluetooth on and started its discovery process, NSDManager broke again. So now I’m NOT getting any services found.

Mark, have you run into a conflict between bluetooth discovery and NSD discovery services?


At April 28, 2021, 11:32pm, mmurphy replied:

No, but I have never tried it, either. :sweat_smile:


At April 29, 2021, 12:17pm, Jan replied:

Okay. Is there a way to return a value from the findDevice once its lambda has the device’s urlFullAddress ?


At April 29, 2021, 12:32pm, mmurphy replied:

If I understand your question, then, as with another post I made here today, you could look into using suspendCancellableCoroutine(), to make findDevice() be a suspend function that adapts the callback-based NsdManager API. See https://wares.commonsware.com/app/internal/book/Coroutines/page/chap-bridge-001.html in Elements of Kotlin Coroutines for an example.