自动优化网站建设,网站建设金手指霸屏,建筑招聘网官网,东莞专业网站建设服务转载必须标明出处 https://blog.csdn.net/jzlhll123
概述
Android 在利用 NsdManager 进行局域网服务发现#xff08;如 mDNS#xff09;时#xff0c;可能遇到的因设备兼容性问题导致的失败。核心结论是#xff1a;为确保应用在绝大多数 Android 设备上稳定运行#xff0…转载必须标明出处 https://blog.csdn.net/jzlhll123概述Android 在利用NsdManager进行局域网服务发现如 mDNS时可能遇到的因设备兼容性问题导致的失败。核心结论是为确保应用在绝大多数 Android 设备上稳定运行强烈建议在使用NsdManager时主动申请并管理WifiManager.MulticastLock。问题现象在使用NsdManager进行服务注册或发现时遇到华为手机android12上的问题mDiscoveryListenernew 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 系统的定制策略密切相关。多播Multicast是发现的基础NsdManager以及 Bonjour/mDNS/DLNA 等服务发现协议依赖于设备接收发往特定多播地址如224.0.0.251的网络数据包。Wi-Fi 芯片的休眠策略为节省电量Android 系统倾向于在空闲时让 Wi-Fi 芯片进入深度休眠状态。在此状态下芯片无法监听网络上的多播数据包。厂商定制的差异不同设备制造商对“何时允许应用唤醒 Wi-Fi 芯片以接收多播包”有不同的实现严格策略部分厂商如下表所列默认完全屏蔽多播包除非应用显式声明需要。宽松策略部分厂商默认允许或策略不那么严格。MulticastLock的作用WifiManager.MulticastLock是一个应用向系统发出的“显式声明”。调用其acquire()方法是在告诉系统“我的应用有正当理由需要接收多播数据包请保持 Wi-Fi 芯片的相关功能活跃。”关键提示依赖设备“可能宽松”的特性进行开发会为应用埋下严重的兼容性隐患。唯一可靠的方法是主动管理MulticastLock。解决方案主动申请 MulticastLock在AndroidManifest.xml中添加以下权限uses-permissionandroid:nameandroid.permission.CHANGE_WIFI_MULTICAST_STATE/以下是在一个Activity或Fragment中集成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);mMulticastLockwifiManager.createMulticastLock(MyAppNsdLock);// 设置引用计数锁推荐保证多次 acquire/release 调用匹配正确mMulticastLock.setReferenceCounted(true);// 2. 初始化 NsdManagermNsdManager(NsdManager)getSystemService(Context.NSD_SERVICE);// 3. 创建服务发现监听器mDiscoveryListenernew 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!nullmMulticastLock.isHeld()){mMulticastLock.release();}}}结论在 Android 碎片化的生态中依赖NsdManager进行局域网服务发现时主动使用WifiManager.MulticastLock是一项关键的防御性编程实践。这能从根本上保证你的功能在绝大多数用户设备上的可靠性避免因厂商定制差异导致的随机性故障从而提供一致的用户体验。