I'm running into a problem with Androids NsdManager when following their tutorial Using Network Service Discovery.
I have a few zeroconf/bonjour hardware devices on my network. From my mac I can discover all of them as expected from my terminal with the following
dns-sd -Z _my-mesh._tcp.
From my Android app's first run I can flawlessly discover these services using NsdManager. However if I restart the application and try again none of the services are found. onDiscoveryStarted
gets called successfully but then nothing else after. While waiting I can confirm from my mac that the services are still successfully there.
I can then turn on my Zeroconf app (on Android) and it will show the services like my mac. When I return to my app I see it immediately receive all the callbacks I expected previously. So I believe something is wrong with my approach, however I'm not sure what. Below is the code I use to discover and resolve services. The view is a giant textview (in a scroll view) I keep writing text to for debugging easier.
import android.annotation.SuppressLint
import android.content.Context
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class MainActivity : AppCompatActivity(),
NsdManager.DiscoveryListener {
private var nsdManager: NsdManager? = null
private var text: TextView? = null
private var isResolving = false
private val services = ArrayList<ServiceWrapper>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
this.text = findViewById(R.id.text)
this.nsdManager = application.getSystemService(Context.NSD_SERVICE) as NsdManager
}
override fun onResume() {
super.onResume()
this.nsdManager?.discoverServices("_my-mesh._tcp.", NsdManager.PROTOCOL_DNS_SD, this)
write("Resume Discovering Services")
}
override fun onPause() {
super.onPause()
this.nsdManager?.stopServiceDiscovery(this)
write("Pause Discovering Services")
}
override fun onServiceFound(serviceInfo: NsdServiceInfo?) {
write("onServiceFound(serviceInfo = $serviceInfo))")
if (serviceInfo == null) {
return
}
add(serviceInfo)
}
override fun onStopDiscoveryFailed(serviceType: String?, errorCode: Int) {
write("onStopDiscoveryFailed(serviceType = $serviceType, errorCode = $errorCode)")
}
override fun onStartDiscoveryFailed(serviceType: String?, errorCode: Int) {
write("onStartDiscoveryFailed(serviceType = $serviceType, errorCode = $errorCode)")
}
override fun onDiscoveryStarted(serviceType: String?) {
write("onDiscoveryStarted(serviceType = $serviceType)")
}
override fun onDiscoveryStopped(serviceType: String?) {
write("onDiscoveryStopped(serviceType = $serviceType)")
}
override fun onServiceLost(serviceInfo: NsdServiceInfo?) {
write("onServiceLost(serviceInfo = $serviceInfo)")
}
private fun createResolveListener(): NsdManager.ResolveListener {
return object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {
write("onResolveFailed(serviceInfo = $serviceInfo, errorCode = $errorCode)")
isResolving = false
resolveNext()
}
override fun onServiceResolved(serviceInfo: NsdServiceInfo?) {
write("onServiceResolved(serviceInfo = $serviceInfo)")
if (serviceInfo == null) {
return
}
for (servicewrapper in services) {
if (servicewrapper.serviceInfo.serviceName == serviceInfo.serviceName) {
servicewrapper.resolve(serviceInfo)
}
}
isResolving = false
resolveNext()
}
}
}
@SuppressLint("SetTextI18n")
private fun write(text: String?) {
this.text?.let {
it.post({
it.text = it.text.toString() + "\n" + text + "\n"
})
}
}
fun add(serviceInfo: NsdServiceInfo) {
for (servicewrapper in services) {
if (servicewrapper.serviceInfo.serviceName == serviceInfo.serviceName) {
return
}
}
services.add(ServiceWrapper(serviceInfo))
resolveNext()
}
@Synchronized
fun resolveNext() {
if (isResolving) {
return
}
isResolving = true
for (servicewrapper in services) {
if (servicewrapper.isResolved) {
continue
}
write("resolving")
this.nsdManager?.resolveService(servicewrapper.serviceInfo, createResolveListener())
return
}
isResolving = false
}
inner class ServiceWrapper(var serviceInfo: NsdServiceInfo) {
var isResolved = false
fun resolve(serviceInfo: NsdServiceInfo) {
isResolved = true
this.serviceInfo = serviceInfo
}
}
}