当前位置: 首页 > news >正文

全网首先发现 android NSDManager做mDNS发现可能无反应

转载必须标明出处 https://blog.csdn.net/jzlhll123

概述

Android 在利用NsdManager进行局域网服务发现(如 mDNS)时,可能遇到的因设备兼容性问题导致的失败。核心结论是:为确保应用在绝大多数 Android 设备上稳定运行,强烈建议在使用NsdManager时,主动申请并管理WifiManager.MulticastLock

问题现象

在使用NsdManager进行服务注册或发现时,遇到华为手机android12上的问题:

mDiscoveryListener=new NsdManager.DiscoveryListener(){// ... 实现 onDiscoveryStarted, onServiceFound, onDiscoveryStopped 等方法@OverridepublicvoidonDiscoveryStarted(String regType){Log.d("NSD","服务发现开始");}@OverridepublicvoidonServiceFound(NsdServiceInfo service){Log.d("NSD","发现服务: "+service.getServiceName());}@OverridepublicvoidonDiscoveryStopped(String serviceType){Log.d("NSD","服务发现停止");}// ... 处理 onServiceLost, onStartDiscoveryFailed, onStopDiscoveryFailed};mNsdManager.discoverServices("_http._tcp.",NsdManager.PROTOCOL_DNS_SD,mDiscoveryListener);

可能会有onDiscoveryStarted的反应,也可能discoverServices之后啥反应也没有。

其他尝试

于是尝试第三方库。

方案成功与否备注
官方API NSDManager95%以上成功率在某些特定的版本和特定的手机上,存在失败的可能性,上面讲到可能存在3%-5%的失败率
https://github.com/jmdns/jmdns 600多星失败❌ 纯java实现,无法解决华为手机发现不了的问题
https://github.com/andriydruk/RxDNSSD 300多星有趣

在做RxDNSSD尝试的时候,有趣的事情发生了,添加上它的发现代码,我们标准的NSDManager也能生效了,不添加它的代码,过一会儿,app又不能发现。因为了解到它这个项目是使用类似苹果的native c代码实现,和一些使用跟底层网络进程逻辑有关,所以一开始推测,是不是它做了什么事情,触发了系统的能力呢?

最终发现了:它的代码里面有一个:

在网上对于MulticastLock,一般都是说的是:

问题的原因呼之欲出。

问题本质

根本原因在于Android 系统的碎片化以及各制造商为优化续航实施的激进省电策略

  • 在某些设备上(如部分华为、HTC、Pixel 机型),服务发现完全失败,无法找到任何设备。
  • 在另一些设备上(如部分三星、小米、旧款 Nexus 机型),相同的代码却能正常工作。
  • 此问题与代码逻辑无关,而与设备硬件、制造商对 Android 系统的定制策略密切相关。
  1. 多播(Multicast)是发现的基础NsdManager以及 Bonjour/mDNS/DLNA 等服务发现协议,依赖于设备接收发往特定多播地址(如224.0.0.251)的网络数据包。
  2. Wi-Fi 芯片的休眠策略:为节省电量,Android 系统倾向于在空闲时让 Wi-Fi 芯片进入深度休眠状态。在此状态下,芯片无法监听网络上的多播数据包。
  3. 厂商定制的差异:不同设备制造商对“何时允许应用唤醒 Wi-Fi 芯片以接收多播包”有不同的实现:
    • 严格策略:部分厂商(如下表所列)默认完全屏蔽多播包,除非应用显式声明需要。
    • 宽松策略:部分厂商默认允许,或策略不那么严格。
  4. MulticastLock的作用WifiManager.MulticastLock是一个应用向系统发出的“显式声明”。调用其acquire()方法是在告诉系统:“我的应用有正当理由需要接收多播数据包,请保持 Wi-Fi 芯片的相关功能活跃。”

关键提示:依赖设备“可能宽松”的特性进行开发,会为应用埋下严重的兼容性隐患。唯一可靠的方法是主动管理MulticastLock

解决方案:主动申请 MulticastLock

AndroidManifest.xml中添加以下权限:

<uses-permissionandroid:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>

以下是在一个ActivityFragment中集成MulticastLock的标准做法:

publicclassServiceDiscoveryActivity extends AppCompatActivity{privateWifiManager.MulticastLock mMulticastLock;privateNsdManager mNsdManager;privateNsdManager.DiscoveryListener mDiscoveryListener;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 1. 初始化并获取 MulticastLockWifiManager wifiManager=(WifiManager)getApplicationContext().getSystemService(Context.WIFI_SERVICE);mMulticastLock=wifiManager.createMulticastLock("MyAppNsdLock");// 设置引用计数锁(推荐),保证多次 acquire/release 调用匹配正确mMulticastLock.setReferenceCounted(true);// 2. 初始化 NsdManagermNsdManager=(NsdManager)getSystemService(Context.NSD_SERVICE);// 3. 创建服务发现监听器mDiscoveryListener=new NsdManager.DiscoveryListener(){// ... 实现 onDiscoveryStarted, onServiceFound, onDiscoveryStopped 等方法@OverridepublicvoidonDiscoveryStarted(String regType){Log.d("NSD","服务发现开始");}@OverridepublicvoidonServiceFound(NsdServiceInfo service){Log.d("NSD","发现服务: "+service.getServiceName());}@OverridepublicvoidonDiscoveryStopped(String serviceType){Log.d("NSD","服务发现停止");}// ... 处理 onServiceLost, onStartDiscoveryFailed, onStopDiscoveryFailed};}@OverrideprotectedvoidonStart(){super.onStart();// 开始发现前,获取锁if(mMulticastLock!=null&&!mMulticastLock.isHeld()){mMulticastLock.acquire();}// 启动服务发现if(mNsdManager!=null){mNsdManager.discoverServices("_http._tcp.",NsdManager.PROTOCOL_DNS_SD,mDiscoveryListener);}}@OverrideprotectedvoidonStop(){super.onStop();// 停止服务发现if(mNsdManager!=null){mNsdManager.stopServiceDiscovery(mDiscoveryListener);}// 及时释放锁,避免不必要的耗电if(mMulticastLock!=null&&mMulticastLock.isHeld()){mMulticastLock.release();}}}

结论

在 Android 碎片化的生态中,依赖NsdManager进行局域网服务发现时,主动使用WifiManager.MulticastLock是一项关键的防御性编程实践。这能从根本上保证你的功能在绝大多数用户设备上的可靠性,避免因厂商定制差异导致的随机性故障,从而提供一致的用户体验。

http://www.cnnetsun.cn/news/134379.html

相关文章:

  • 安全副业指南:漏洞挖掘 / 技术博客 / 竞赛奖金实战,有什么区别,哪个最赚钱?
  • MathLive 终极指南:2025年最简单上手的网页数学公式编辑器
  • 终极微信助手:6大智能功能让Mac微信更好用
  • 盘搜联盟 一键直达海量资源,全网最强百度网盘搜索神器!
  • 37、高效学习实践:成为学习大师
  • 网页小说自由转换神器:一键打造你的专属离线图书馆
  • 边缘AI如何真正嵌入终端?基于ESP32-S3的软硬件一体化方案详解
  • LoRA vs 全量微调:大模型微调的高效选择指南!大模型微调
  • Jellyfin Kodi插件完整安装配置教程:打造完美家庭影院体验
  • 【期末复习01_02】-结构类算法题
  • Reactor Core性能基准测试深度解析:从原理到实战的性能优化指南
  • KlipperScreen安装终极指南:零基础打造3D打印触摸屏界面
  • 群晖视频信息插件终极配置:高效管理影视库元数据
  • 瞧一瞧原圈科技如何在AI营销内容生产的系统化破局
  • 联想拯救者BIOS高级设置解锁工具完全指南
  • Unity内置着色器完全使用指南
  • 【复现】基于非线性模型预测控制和事件触发通信的AUV编队多智能体协同控制路径跟踪研究附Matlab代码
  • 3分钟玩转树莓派NFC读卡器:MFRC522-python极速上手攻略
  • MuJoCo无头渲染完全指南:告别图形界面的物理仿真可视化
  • 【PCL点云配准】ICP收敛判断
  • Android混合开发终极指南:AgentWeb实现原生与Web的无缝融合
  • 【程序员必学】智能体(Agent)技术:大模型落地的核心载体与应用【收藏指南】
  • 学生综合测评|基于springboot + vue学生综合测评管理系统(源码+数据库+文档)
  • Waydroid完整使用指南:在Linux系统上快速运行Android应用
  • 基于SSM的一线式酒店管理系统-计算机毕业设计源码+LW文档分享
  • 异常处理框架设计:全局异常捕获与统一错误码
  • 批量压缩对象存储中视频
  • ytDownloader视频下载全攻略:从入门到精通的完整指南
  • OPC Data Client+MQTT协议:工业数据连接与传输一体化解决方案
  • 【雷达检测】基于Swerling目标模型的雷达信号检测附Matlab代码和报告