You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Oppo Android14设备NSD Discovery无法工作的问题排查

NSD Discovery无法在Android14的Oppo设备上发现服务

我按照官方文档实现了NSD服务注册与发现逻辑,代码在Pixel(Android14)、华为(Android9)、传音Infinix(Android11)等设备上都能正常工作,但唯独Android14的Oppo设备无法发现任何服务。

实现代码

NsdDiscovery类

class NsdDiscovery(private val context: Context) {

    private var mHostAddress: InetAddress? = null
    private var mHostPort = 0
    private lateinit var mNsdManager: NsdManager
    private var isDiscovering = false
    private var mRandom = 0
    private val mDiscoveryListener = DiscoveryListener()

    fun updateConnectionListener(str: String, i: Int) {
        Log.d(TAG, "updateConnectionListener: $str $i")
        LocalBroadcastManager.getInstance(context).sendBroadcast(
            Intent(BroadcastActions.DEVICE_CONNECTED.toString())
                .putExtra(Constants.IP_ADDRESS.name, str)
                .putExtra(Constants.PORT.name, i)
        )
    }

    inner class DiscoveryListener : NsdManager.DiscoveryListener {

        override fun onDiscoveryStarted(serviceType: String?) {
            Log.d(TAG, "onDiscoveryStarted: ")
            serviceType?.let {
                Log.d(TAG, "onDiscoveryStarted: $it")
            }
        }

        override fun onServiceFound(serviceInfo: NsdServiceInfo?) {
            Log.d(TAG, "onServiceFound: $serviceInfo")
            serviceInfo?.let { info ->
                Log.d(TAG, "onServiceFound: service info $info")
                if (info.serviceType.equals(SERVICE_TYPE)) {
                    Log.d(TAG, "onServiceFound: service type matches")
                    if (info.serviceName.equals(SERVICE_NAME)) {
                        Log.d(TAG, "onServiceFound: Listening Same Machine")
                        return
                    }
                    mNsdManager.resolveService(info, MyResolveListener())
                    return
                }
            }
        }

        inner class MyResolveListener : NsdManager.ResolveListener {
            override fun onResolveFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {
                Log.d(TAG, "onResolveFailed: $errorCode")
                serviceInfo?.let {
                    Log.d(TAG, "onResolveFailed: Listening service $it")
                }
            }

            override fun onServiceResolved(serviceInfo: NsdServiceInfo?) {
                Log.d(TAG, "onServiceResolved: $serviceInfo")
                serviceInfo?.let { nsdServiceInfo ->
                    if (nsdServiceInfo.serviceName.equals(mRandom.toString())) {
                        Log.d(TAG, "onServiceResolved: Service Matched: $mRandom")
                        if (nsdServiceInfo.serviceName.equals(SERVICE_NAME)) {
                            Log.d(TAG, "onServiceResolved: Listening Port $nsdServiceInfo")
                            return
                        }
                        var inetAddress: InetAddress? = null
                        try {
                            inetAddress = InetAddress.getByName(nsdServiceInfo.host.hostAddress)
                        } catch (e: UnknownHostException) {
                            e.printStackTrace()
                        }
                        try {
                            inetAddress?.hostAddress
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
                        mHostPort = nsdServiceInfo.port
                        mHostAddress = nsdServiceInfo.host
                        Log.d(TAG, "onServiceResolved: Listening Port: $mHostPort")
                        Log.d(TAG, "onServiceResolved: Listening IP: $mHostAddress")
                        Toast.makeText(context, "Obtained IP: \n $mHostAddress",      Toast.LENGTH_SHORT).show()
                        if (mHostAddress is Inet6Address) {
                            stopDiscoveringServices()
                            Handler(Looper.getMainLooper()).postDelayed({
                                discoverServices(mRandom)
                            }, 3000)
                        }
                        try {
                            val localHost = InetAddress.getLocalHost()
                            Log.d(TAG, "onServiceResolved: ${localHost.hostAddress}")
                        } catch (e: UnknownHostException) {
                            e.printStackTrace()
                        }
                        if (mHostAddress!!.hostAddress!! == STATIC_IP || mHostAddress is Inet6Address) {
                            Log.d(TAG, "onServiceResolved: equals to static ip or ipv6")
                            return
                        }
                        updateConnectionListener(mHostAddress!!.hostAddress, nsdServiceInfo.port)
                    }
                }
            }
        }

        override fun onServiceLost(serviceInfo: NsdServiceInfo?) {
            Log.d(TAG, "onServiceLost: $serviceInfo")
        }

        override fun onDiscoveryStopped(serviceType: String?) {
            Log.d(TAG, "onDiscoveryStopped: $serviceType")
        }

        override fun onStartDiscoveryFailed(serviceType: String?, errorCode: Int) {
            Log.d(TAG, "onStartDiscoveryFailed: $errorCode")
            stopDiscoveringServices()
        }

        override fun onStopDiscoveryFailed(serviceType: String?, errorCode: Int) {
            Log.d(TAG, "onStopDiscoveryFailed: $errorCode")
            stopDiscoveringServices()
        }

    }

    fun discoverServices(i: Int) {
        Log.d(TAG, "discoverServices: $i")
        mRandom = i
        mNsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager
        if (isDiscovering) {
            Log.d(TAG, "discoverServices: Already Discovering")
            return
        }
        isDiscovering = true
        mNsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener)
        Log.d(TAG, "discoverServices: started discovering")
    }

    fun stopDiscoveringServices() {
        if (isDiscovering) {
            isDiscovering = false
            mNsdManager.stopServiceDiscovery(mDiscoveryListener)
            Log.d(TAG, "stopDiscoveringServices: discovery stoped")
        }
    }

    companion object {
        private const val TAG = "NsdDiscovery"
        private const val SERVICE_NAME = "Client Device"
        private const val SERVICE_TYPE = "_http._tcp."
        private const val STATIC_IP = "192.168.43.1"
    }
}

NsdService类

class NsdService(private val context: Context) {

    private lateinit var mNsdManager: NsdManager
    private var SERVICE_NAME = "Server device"
    private val SERVICE_TYPE = "_http._tcp."
    private var isServiceRegistered = false
    private var tryUnregister = 0
    private var mPort = 0
    private var mRandomName = SERVICE_NAME

    private var mRegistrationListener: RegistrationListener? = object : RegistrationListener {
        override fun onServiceRegistered(serviceInfo: NsdServiceInfo?) {
            isServiceRegistered = true
            serviceInfo?.let {
                SERVICE_NAME = serviceInfo.serviceName
                Log.d(TAG, "onServiceRegistered: Service Name: ${serviceInfo.serviceName}")
                LocalBroadcastManager.getInstance(context)
                    .sendBroadcast(Intent(BroadcastActions.SERVICE_REGISTERED.name))
            }
        }

        override fun onRegistrationFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {
            Log.d(TAG, "onRegistrationFailed: ")
            Handler(Looper.getMainLooper()).postDelayed({
                registerService(mPort, mRandomName)
            }, 2000)
        }

        override fun onServiceUnregistered(serviceInfo: NsdServiceInfo?) {
            Log.d(TAG, "onServiceUnregistered: ")
            serviceInfo?.let {
                Log.d(TAG, "onServiceUnregistered: ${serviceInfo.serviceName}")
            }
        }

        override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) {
            Log.d(TAG, "onUnregistrationFailed: ")
            isServiceRegistered = true
            if (tryUnregister < 3) {
                unRegisterService()
                tryUnregister++
            }
        }
    }

    fun registerService(port: Int, random: String) {
        mNsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager
        if (!isServiceRegistered) {
            mPort = port
            val nsdServiceInfo = NsdServiceInfo().apply {
                serviceName = random
                serviceType = SERVICE_TYPE
                setPort(port)
            }
            mNsdManager.registerService(nsdServiceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener)
        }
    }

    fun unRegisterService() {
        if (isServiceRegistered) {
            if (mRegistrationListener != null) {
                mNsdManager.unregisterService(mRegistrationListener)
            }
            isServiceRegistered = false
        }
    }

    companion object {
        private const val TAG = "NsdService"
    }
}

针对性排查与解决方案

1. 检查Oppo系统的权限与隐私限制

Oppo的ColorOS对本地网络发现有额外管控:

  • 确认应用已开启位置权限(NSD依赖位置服务扫描本地网络)
  • 关闭「隐私替身」功能,该功能可能干扰网络扫描
  • 在「设置 > 应用 > 你的应用 > 权限管理」中,确认授予「附近设备」权限(Android12+新增,ColorOS会强化该权限校验)

2. 取消IPv6地址过滤

Oppo设备可能默认优先使用IPv6,当前代码中直接过滤IPv6地址导致服务被跳过:

  • 注释掉onServiceResolved中if (mHostAddress is Inet6Address)的重启扫描逻辑
  • 或修改代码兼容IPv6地址处理,不要直接忽略

3. 确保服务名称唯一性

ColorOS对重复服务名称的兼容性较差,修改NsdService的注册逻辑:

// 在registerService方法中,给服务名称添加随机后缀
serviceName = "$random-${System.currentTimeMillis()}"

4. 兼容多种NSD协议

尝试同时使用DNS-SD和MDNS协议,修改扫描与注册时的参数:

// 替换原有的PROTOCOL_DNS_SD
NsdManager.PROTOCOL_DNS_SD or NsdManager.PROTOCOL_MDNS

5. 增加错误码日志排查

在onStartDiscoveryFailed中添加错误码解析,定位具体失败原因:

override fun onStartDiscoveryFailed(serviceType: String?, errorCode: Int) {
    val errorMsg = when(errorCode) {
        NsdManager.FAILURE_ALREADY_ACTIVE -> "扫描已在进行"
        NsdManager.FAILURE_INTERNAL_ERROR -> "系统内部错误"
        NsdManager.FAILURE_SERVICE_NOT_FOUND -> "未找到匹配服务"
        else -> "未知错误:$errorCode"
    }
    Log.e(TAG, "扫描启动失败: $errorMsg")
    stopDiscoveringServices()
}

6. 验证网络环境

确保Oppo设备与服务端设备连接同一WiFi网络,且路由器未开启AP隔离(部分路由器默认开启,会阻止设备间本地通信)

内容的提问来源于stack exchange,提问作者Adnan Aslam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 04:35:00