问题描述
在应该在 Android 6+ 上运行的 Android 应用程序上,我尝试使用网络服务发现来检查我的网络上是否有一些设备可用。
基于 this part of the official documentation,我创建了这个小 POC:
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "POC"
private const val SERVICE_TYPE = "_http._tcp."
}
private val servicetoResolve = mutablelistof<NsdServiceInfo>()
private var index = 0
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.discover_btn).setonClickListener {
search()
}
}
private fun search() {
lifecycleScope.launch(dispatchers.IO) {
val serviceInfo = NsdServiceInfo().apply {
serviceName = "MyServiceTest"
serviceType = SERVICE_TYPE
port = 80
}
val registrationListener = object : NsdManager.RegistrationListener {
override fun onServiceRegistered(NsdServiceInfo: NsdServiceInfo) {}
override fun onRegistrationFailed(serviceInfo: NsdServiceInfo,errorCode: Int) {}
override fun onServiceUnregistered(arg0: NsdServiceInfo) {
}
override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo,errorCode: Int) {}
}
val nsdManager = (getSystemService(Context.NSD_SERVICE) as NsdManager).apply {
registerService(serviceInfo,NsdManager.PROTOCOL_DNS_SD,registrationListener)
}
// Instantiate a new discoveryListener
val discoveryListener = object : NsdManager.discoveryListener {
override fun ondiscoveryStarted(regType: String) {
}
override fun onServiceFound(service: NsdServiceInfo) {
servicetoResolve.add(service)
}
override fun onServiceLost(service: NsdServiceInfo) {}
override fun ondiscoveryStopped(serviceType: String) {}
override fun onStartdiscoveryFailed(serviceType: String,errorCode: Int) {
nsdManager.stopServicediscovery(this)
}
override fun onStopdiscoveryFailed(serviceType: String,errorCode: Int) {
nsdManager.stopServicediscovery(this)
}
}
nsdManager.discoverServices(SERVICE_TYPE,discoveryListener)
delay(5000)
resolveService(nsdManager)
}
}
private fun resolveService(nsdManager: NsdManager) {
if (servicetoResolve.size > index) {
nsdManager.resolveService(servicetoResolve[index],object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo?,errorCode: Int) {
index++
resolveService(nsdManager)
}
override fun onServiceResolved(serviceInfo: NsdServiceInfo?) {
Log.d(TAG,"onServiceResolved : $serviceInfo || ${serviceInfo?.attributes}")
index++
resolveService(nsdManager)
}
})
} else {
Log.d(TAG,"nothing to resolve")
}
}
}
此代码在 Android 7 到 11 上正常工作。但在 Android 6 上无法正常工作。我可以正确解析服务,但根据日志,它不包含任何属性:
Log.d(TAG,"onServiceResolved : $serviceInfo || ${serviceInfo?.attributes}")
例如,Android 6.0 上的结果:
onServiceResolved : name: TJA470 (TJA470-F40103),type: ._http._tcp,host: /10.0.0.177,port: 80 || {}
这里是 Android 11 上的结果:
onServiceResolved : name: TJA470 (TJA470-F40103),port: 80,txtRecord: 1device.type=urn:schemas-hager-com:device:tja470: 1device.modelName=TJA470 export=Yes$device.modelURL=http://www.hager.comdevice.hostName=TJA470-F40103device.manufacturer=Hagerdevice.ip=10.0.0.1771device.modelDescription=Hager Pilot Configuration+device.manufacturerURL=http://www .hager.com(device.UDN=uuid:M2VSZEVUELAJYECHAJLU1708 device.UPC=470,device.serialNumber=M2VSZEVUELAJYECHAJLU1708device.modelNumber=3.1.15'presentationURL=http://TJA470-F40103.JA70TName=80TJA470 -F40103) || {device.type=[117,114,110,58,115,99,104,101,109,97,45,103,9,41,100,118,105,116,106,52,55,48,49],device.modelName=[84,74,65,48],export=[89,115],device.modelURL=[104,112,47,119,46,140,111,109],device.hostName=[84,70,49,51],device.manufacturer=[72,114],device.ip=[49,55],device.modelDescription=[72,1 14,32,80,108,67,102,117,er.110 uact[102,117] 116,device.UDN=15,171 100,77,50,86,83,90,69,85,76,89,72,8,56],device.UPC=[52,device.serialNumber=[77,device.modelNumber=[51,53],presentationURL=[104,84,51,56,device.friendlyName=[84,40,41]}
如您所见,这些属性出现在 Android 11 上,而不是在 Android 6.0 上。是 Android 6.0 上的 NSD_SERVICE 的问题还是我开发的 POC 的问题?
NsdServiceInfo
类已在 API 16 中添加,而 getAttributes
方法已在 API 21 中添加。因此通常一切都应在 Android 6.0 上正常工作:(
解决方法
问题不在我的代码中,而是在 Android SDK 中,根据 this ticket。
在 Android 5 和 6 上,我需要使用第三方库,例如 Jmdns。