stack overflow有介绍,这个原因可能是系统同时发现了多个
kotlin
discoveryListener = object : NsdManager.DiscoveryListener {
override fun onServiceFound(service: NsdServiceInfo) {
onServiceFoundInfo(service) //解析info
}
....
}
fun onServiceFoundInfo() {
//开始解析
nsdManager.resolveService(next, object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
loge(TAG) { "Failed to resolve service: ${serviceInfo.serviceName}, error: $errorCode" }
}
override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
//解析成功
})
}
如果你这里直接开始解析,在某些手机上,可能就报错了。告诉你,解析出错,errorCode=3。
因为可能同时有多个onServiceFound过来,同时,resolveService就会出错。
解决办法:串行执行。
这里采用kotlin的suspendCancellableCoroutine + LinkedBlockingQueue解决。
kotlin
private val mResolveQueue = LinkedBlockingQueue<NsdServiceInfo>()
private val mIsResolving = AtomicBoolean(false)
discoveryListener = object : NsdManager.DiscoveryListener {
override fun onServiceFound(service: NsdServiceInfo) {
onServiceFoundInfo(service)
}
....
}
private fun onServiceFoundInfo(service: NsdServiceInfo) {
mResolveQueue.offer(service) //内部有锁的
processResolveQueue()
}
private fun processResolveQueue() {
if (!mIsResolving.compareAndSet(false, true)) return
scope.launch { //子线程
while (true) {
val next = mResolveQueue.poll() ?: break
val resolved = suspendCancellableCoroutine { cont ->
try {
nsdManager.resolveService(next, object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
loge(TAG) { "Failed to resolve service: ${serviceInfo.serviceName}, error: $errorCode" }
if (cont.isActive) cont.resume(null)
}
override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
if (cont.isActive) cont.resume(serviceInfo)
}
})
} catch (e: Exception) {
loge(TAG, e) { "Failed to resolve service: ${next.serviceName}, error: ${e.message}" }
if (cont.isActive) (
//if(!e.message.isNullOrEmpty()) cont.resumeWithException(e) else cont.resume(null)
cont.resume(null) //不做异常抛出
)
}
}
if (resolved != null) {
handleResolvedService(resolved) //todo 自己的逻辑
}
}
mIsResolving.set(false)
if (mResolveQueue.isNotEmpty()) {
processResolveQueue()
}
}
}