gpt4 book ai didi

android - NSdManager ResolveListener 错误代码 3 : Failure Already active

转载 作者:行者123 更新时间:2023-12-03 22:04:39 26 4
gpt4 key购买 nike

我在 Android 应用程序中使用 NsdManager 来发现由我开发的另一台设备发布的 NSD 服务。我只在 Android App 上进行服务发现(这方面不需要服务注册)。网络上同时发布了多个同类型服务的实例。

我开始使用 Google 提供的示例代码 (https://developer.android.com/training/connect-devices-wirelessly/nsd),但由于同时将同一个解析器对象重用于多个服务解析,我遇到了 fatal error 。
然后我发现有几个人建议每次都创建一个新的解析器对象(如 Listener already in use (Service Discovery) )。

我这样做了, fatal error 被 Resolve Failure 错误代码 3 取代,这意味着解决过程处于 Activity 状态。比以前好多了,但只有第一个服务被解决了,其余的都因为这个失败而被忽略了。

然后我发现有人建议通过递归地重新发送解析请求直到最终解决(NSNetworkManager.ResolveListener messages Android)来对错误代码 3 进行特殊处理。

我在 Kotlin 中实现了这个解决方案,它有点工作,但我并不满意,因为:

  • 我相信我正在创建很多额外的 Resolver 对象
    而且我不确定它们后来是否被垃圾收集。
  • 我在一个循环中重试了几次,可能会导致额外的和
    对设备和网络造成不必要的负担。不确定我是否
    应该在再次调用服务解析之前添加一个短暂的 sleep 。
  • 如果有一些网络问题,程序可能会尝试数千
    多次解决相同的服务,而不是仅仅放弃
    解决并等待再次发现该服务。

  • RxBonjour2 的人提供了一个更复杂、更强大的解决方案,但它对我来说太复杂了: https://github.com/mannodermaus/RxBonjour/blob/2.x/rxbonjour-drivers/rxbonjour-driver-nsdmanager/src/main/kotlin/de/mannodermaus/rxbonjour/drivers/nsdmanager/NsdManagerDiscoveryEngine.kt

    我对 Google 的官方示例没有正确处理这些问题感到沮丧。 nsd_chat 示例使用单个解析器对象,并且在网络上以相同类型发布多个具有相同类型的服务时失败。

    您能提出更好的解决方案吗?或者对我下面的代码有什么改进?
    import android.app.Application
    import android.content.Context
    import android.net.nsd.NsdManager
    import android.net.nsd.NsdServiceInfo
    import androidx.lifecycle.AndroidViewModel
    import timber.log.Timber


    class ViewModel(application: Application) : AndroidViewModel(application) {

    // Get application context
    private val myAppContext: Context = getApplication<Application>().applicationContext

    // Declare DNS-SD related variables for service discovery
    var nsdManager: NsdManager? = null
    private var discoveryListener: NsdManager.DiscoveryListener? = null

    // Constructor for the View Model that is run when the view model is created
    init {

    // Initialize DNS-SD service discovery
    nsdManager = myAppContext.getSystemService(Context.NSD_SERVICE) as NsdManager?

    initializeDiscoveryListener()

    // Start looking for available services in the network
    nsdManager?.discoverServices(NSD_SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)

    }

    // Instantiate DNS-SD discovery listener
    // used to discover available Sonata audio servers on the same network
    private fun initializeDiscoveryListener() {

    // Instantiate a new DiscoveryListener
    discoveryListener = object : NsdManager.DiscoveryListener {

    override fun onDiscoveryStarted(regType: String) {
    // Called as soon as service discovery begins.
    Timber.d("Service discovery started: $regType")
    }

    override fun onServiceFound(service: NsdServiceInfo) {
    // A service was found! Do something with it
    Timber.d("Service discovery success: $service")
    when {
    service.serviceType != NSD_SERVICE_TYPE ->
    // Service type is not the one we are looking for
    Timber.d("Unknown Service Type: ${service.serviceType}")
    service.serviceName.contains(NSD_SERVICE_NAME) ->
    // Both service type and service name are the ones we want
    // Resolve the service to get all the details
    startResolveService(service)
    else ->
    // Service type is ours but not the service name
    // Log message but do nothing else
    Timber.d("Unknown Service Name: ${service.serviceName}")
    }
    }

    override fun onServiceLost(service: NsdServiceInfo) {
    onNsdServiceLost(service)
    }

    override fun onDiscoveryStopped(serviceType: String) {
    Timber.i("Discovery stopped: $serviceType")
    }

    override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
    Timber.e("Start Discovery failed: Error code: $errorCode")
    nsdManager?.stopServiceDiscovery(this)
    }

    override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
    Timber.e("Stop Discovery failed: Error code: $errorCode")
    nsdManager?.stopServiceDiscovery(this)
    }
    }
    }

    fun startResolveService(service: NsdServiceInfo) {

    val newResolveListener = object : NsdManager.ResolveListener {

    override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
    // Called when the resolve fails. Use the error code to determine action.
    when (errorCode) {
    NsdManager.FAILURE_ALREADY_ACTIVE -> {
    // Resolver was busy
    Timber.d("Resolve failed: $serviceInfo - Already active")
    // Just try again...
    startResolveService(serviceInfo)
    }
    else ->
    Timber.e("Resolve failed: $serviceInfo - Error code: $errorCode")
    }
    }

    override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
    onNsdServiceResolved(serviceInfo)
    }
    }

    nsdManager?.resolveService(service, newResolveListener)
    }

    companion object {

    // We'll only search for NDS services of this type
    const val NSD_SERVICE_TYPE: String = "_servicetype._tcp."
    // and whose names start like this
    const val NSD_SERVICE_NAME: String = "ServiceName-"
    }

    override fun onCleared() {
    try {
    nsdManager?.stopServiceDiscovery(discoveryListener)
    } catch (ignored: Exception) {
    // "Service discovery not active on discoveryListener",
    // thrown if starting the service discovery was unsuccessful earlier
    }
    Timber.d("onCleared called")
    super.onCleared()
    }

    fun onNsdServiceResolved(serviceInfo: NsdServiceInfo) {
    // Logic to handle a new service
    Timber.d("Resolve Succeeded: $serviceInfo")
    }

    fun onNsdServiceLost(service: NsdServiceInfo) {
    // Logic to handle when the network service is no longer available
    Timber.d("Service lost: $service")
    }

    }

    最佳答案

    我通过以下方式解决了这个问题:

  • 创建线程安全队列来存储待解决的服务
  • 使用线程安全列表存储已解析服务的列表
  • 使用原子 bool 标志查看 ResolveListener 何时忙

  • 为了使解决方案更通用,我构建了一个 NdsHelper 抽象类。它有 2 个必须重写的函数:onNsdServiceResolved(NsdServiceInfo) 和 onNsdServiceLost(NsdServiceInfo)。

    我使用 Timber 来记录消息,但您可以用标准 Log 函数替换它们。

    这是 NsdHelper 类(Kotlin 代码):
    import android.content.Context
    import android.net.nsd.NsdManager
    import android.net.nsd.NsdServiceInfo
    import timber.log.Timber
    import java.util.*
    import java.util.concurrent.ConcurrentLinkedQueue
    import java.util.concurrent.atomic.AtomicBoolean
    import kotlin.collections.ArrayList

    abstract class NsdHelper(val context: Context) {

    // Declare DNS-SD related variables for service discovery
    val nsdManager: NsdManager? = context.getSystemService(Context.NSD_SERVICE) as NsdManager?
    private var discoveryListener: NsdManager.DiscoveryListener? = null
    private var resolveListener: NsdManager.ResolveListener? = null
    private var resolveListenerBusy = AtomicBoolean(false)
    private var pendingNsdServices = ConcurrentLinkedQueue<NsdServiceInfo>()
    var resolvedNsdServices: MutableList<NsdServiceInfo> = Collections.synchronizedList(ArrayList<NsdServiceInfo>())

    companion object {

    // Type of services to look for
    const val NSD_SERVICE_TYPE: String = "_myservicetype._tcp."
    // Services' Names must start with this
    const val NSD_SERVICE_NAME: String = "MyServiceName-"
    }

    // Initialize Listeners
    fun initializeNsd() {
    // Initialize only resolve listener
    initializeResolveListener()
    }

    // Instantiate DNS-SD discovery listener
    // used to discover available Sonata audio servers on the same network
    private fun initializeDiscoveryListener() {

    // Instantiate a new DiscoveryListener
    discoveryListener = object : NsdManager.DiscoveryListener {

    override fun onDiscoveryStarted(regType: String) {
    // Called as soon as service discovery begins.
    Timber.d("Service discovery started: $regType")
    }

    override fun onServiceFound(service: NsdServiceInfo) {
    // A service was found! Do something with it
    Timber.d("Service discovery success: $service")

    if ( service.serviceType == NSD_SERVICE_TYPE &&
    service.serviceName.startsWith(NSD_SERVICE_NAME) ) {
    // Both service type and service name are the ones we want
    // If the resolver is free, resolve the service to get all the details
    if (resolveListenerBusy.compareAndSet(false, true)) {
    nsdManager?.resolveService(service, resolveListener)
    }
    else {
    // Resolver was busy. Add the service to the list of pending services
    pendingNsdServices.add(service)
    }
    }
    else {
    // Not our service. Log message but do nothing else
    Timber.d("Not our Service - Name: ${service.serviceName}, Type: ${service.serviceType}")
    }
    }

    override fun onServiceLost(service: NsdServiceInfo) {
    Timber.d("Service lost: $service")

    // If the lost service was in the queue of pending services, remove it
    var iterator = pendingNsdServices.iterator()
    while (iterator.hasNext()) {
    if (iterator.next().serviceName == service.serviceName)
    iterator.remove()
    }

    // If the lost service was in the list of resolved services, remove it
    synchronized(resolvedNsdServices) {
    iterator = resolvedNsdServices.iterator()
    while (iterator.hasNext()) {
    if (iterator.next().serviceName == service.serviceName)
    iterator.remove()
    }
    }

    // Do the rest of the processing for the lost service
    onNsdServiceLost(service)
    }

    override fun onDiscoveryStopped(serviceType: String) {
    Timber.i("Discovery stopped: $serviceType")
    }

    override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
    Timber.e("Start Discovery failed: Error code: $errorCode")
    stopDiscovery()
    }

    override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
    Timber.e("Stop Discovery failed: Error code: $errorCode")
    nsdManager?.stopServiceDiscovery(this)
    }
    }
    }

    // Instantiate DNS-SD resolve listener to get extra information about the service
    private fun initializeResolveListener() {
    resolveListener = object : NsdManager.ResolveListener {

    override fun onServiceResolved(service: NsdServiceInfo) {
    Timber.d("Resolve Succeeded: $service")

    // Register the newly resolved service into our list of resolved services
    resolvedNsdServices.add(service)

    // Process the newly resolved service
    onNsdServiceResolved(service)

    // Process the next service waiting to be resolved
    resolveNextInQueue()
    }

    override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
    // Called when the resolve fails. Use the error code to debug.
    Timber.e("Resolve failed: $serviceInfo - Error code: $errorCode")

    // Process the next service waiting to be resolved
    resolveNextInQueue()
    }
    }
    }

    // Start discovering services on the network
    fun discoverServices() {
    // Cancel any existing discovery request
    stopDiscovery()

    initializeDiscoveryListener()

    // Start looking for available audio channels in the network
    nsdManager?.discoverServices(NSD_SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)
    }

    // Stop DNS-SD service discovery
    fun stopDiscovery() {
    if (discoveryListener != null) {
    try {
    nsdManager?.stopServiceDiscovery(discoveryListener)
    } finally {
    }
    discoveryListener = null
    }
    }

    // Resolve next NSD service pending resolution
    private fun resolveNextInQueue() {
    // Get the next NSD service waiting to be resolved from the queue
    val nextNsdService = pendingNsdServices.poll()
    if (nextNsdService != null) {
    // There was one. Send to be resolved.
    nsdManager?.resolveService(nextNsdService, resolveListener)
    }
    else {
    // There was no pending service. Release the flag
    resolveListenerBusy.set(false)
    }
    }

    // Function to be overriden with custom logic for new service resolved
    abstract fun onNsdServiceResolved(service: NsdServiceInfo)

    // Function to be overriden with custom logic for service lost
    abstract fun onNsdServiceLost(service: NsdServiceInfo)
    }

    这就是如何从 ViewModel 使用它(或者从一个 Activity 或 fragment ,如果你改变从哪里调用不同的帮助方法):
    import android.app.Application
    import android.content.Context
    import android.content.Intent
    import android.net.ConnectivityManager
    import android.net.nsd.NsdServiceInfo
    import androidx.lifecycle.AndroidViewModel
    import timber.log.Timber
    import java.util.*


    class MyViewModel(application: Application) : AndroidViewModel(application) {

    // Get application context
    private val myAppContext: Context = getApplication<Application>().applicationContext

    // Declare NsdHelper object for service discovery
    private val nsdHelper: NsdHelper? = object : NsdHelper(myAppContext) {

    override fun onNsdServiceResolved(service: NsdServiceInfo) {
    // A new network service is available

    // Put your custom logic here!!!

    }

    override fun onNsdServiceLost(service: NsdServiceInfo) {
    // A network service is no longer available

    // Put your custom logic here!!!

    }
    }

    // Block that is run when the view model is created
    init {

    // Initialize DNS-SD service discovery
    nsdHelper?.initializeNsd()

    // Start looking for available audio channels in the network
    nsdHelper?.discoverServices()

    }

    // Called when the view model is destroyed
    override fun onCleared() {
    nsdHelper?.stopDiscovery()
    Timber.d("onCleared called")
    super.onCleared()
    }

    }

    关于android - NSdManager ResolveListener 错误代码 3 : Failure Already active,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57940021/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com