14、网络与连接兼容性:WLAN直连、蓝牙权限细化,DNS-over-HTTP/TLS的适配

各位好,今天我们来聊聊网络与连接这块的兼容性适配。说实话,这部分内容在 Android 系统升级中经常被忽视,但一旦出问题,用户反馈往往特别激烈——毕竟谁都不想手机连不上 Wi-Fi 或者蓝牙耳机突然断连。

我个人习惯把网络连接兼容性分成三个维度来看:WLAN 直连(P2P)蓝牙权限细化、以及DNS-over-HTTP/TLS(DoH/DoT)。这三块在 Android 14、15 中都有不小的变化,咱们一个一个说。

核心要点:网络连接兼容性适配的核心是「权限模型变化」和「底层协议栈升级」。前者影响应用能否正常发现和连接设备,后者影响网络通信的安全性和稳定性。

14.1 WLAN 直连(Wi-Fi P2P)的适配

WLAN 直连,说白了就是手机之间不经过路由器直接通信。这个功能在文件传输、投屏、打印等场景下非常常用。

我在项目中遇到过一个问题:某款手机升级到 Android 14 后,Wi-Fi P2P 扫描总是超时。查了半天,发现是新的位置权限限制导致的。

14.1.1 权限模型变化

Android 14 开始,WLAN 直连的权限要求变得更严格了:

  • NEARBY_WIFI_DEVICES:必须声明,用于发现和连接附近的 Wi-Fi 设备
  • ACCESS_FINE_LOCATION:不再强制要求(但部分厂商仍可能需要)
  • CHANGE_WIFI_STATE:仍然需要,用于启用/禁用 Wi-Fi P2P

嗯,这里要注意:虽然 Google 说不再需要位置权限,但国内很多厂商的 ROM 还是要求位置权限才能扫描到设备。我建议你们在适配时做兼容处理——先尝试不带位置权限的扫描,如果失败再提示用户授权。

我的经验:在 Android 14 上,如果只声明 NEARBY_WIFI_DEVICES 权限,部分三星和小米机型会扫描不到设备。解决方案是在 AndroidManifest 中同时声明 ACCESS_FINE_LOCATION,但用 maxSdkVersion="33" 限制其只在旧版本生效。

14.1.2 代码适配示例

// Android 14+ 的 WLAN 直连权限声明
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
    android:usesPermissionFlags="neverForLocation" />

// 兼容旧版本的声明
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
    android:maxSdkVersion="33" />

// 运行时权限请求(Kotlin)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    requestPermissions(arrayOf(
        Manifest.permission.NEARBY_WIFI_DEVICES
    ), REQUEST_CODE_NEARBY_WIFI)
} else {
    requestPermissions(arrayOf(
        Manifest.permission.ACCESS_FINE_LOCATION
    ), REQUEST_CODE_LOCATION)
}

14.2 蓝牙权限细化

蓝牙这块的变化,说白了就是权限颗粒度更细了。以前一个 BLUETOOTH 权限搞定所有,现在不行了。

我记得 Android 12 开始引入了 BLUETOOTH_SCAN、BLUETOOTH_ADVERTISE、BLUETOOTH_CONNECT 这三个权限。到了 Android 14,这些权限的使用场景更加明确。

14.2.1 权限分类与使用场景

权限 使用场景 是否需要位置权限
BLUETOOTH_SCAN 扫描附近的蓝牙设备 Android 12+ 不需要(需声明 neverForLocation)
BLUETOOTH_ADVERTISE 广播自身,让其他设备发现 不需要
BLUETOOTH_CONNECT 连接已发现的设备 不需要

避坑指南:我曾经遇到一个案例——某款运动 App 在 Android 14 上扫描不到蓝牙心率带。原因是开发者只申请了 BLUETOOTH_CONNECT 权限,没有申请 BLUETOOTH_SCAN。记住:扫描和连接是两个不同的权限,缺一不可。

14.2.2 适配要点

  • 权限分组请求:建议将 BLUETOOTH_SCAN 和 BLUETOOTH_CONNECT 一起请求,避免用户多次授权
  • neverForLocation 标记:如果你的应用不需要通过蓝牙获取位置信息,一定要加上这个标记,否则会被 Google Play 拒绝
  • 兼容旧版本:Android 11 及以下仍然使用 BLUETOOTH 和 BLUETOOTH_ADMIN 权限

14.3 DNS-over-HTTP/TLS 的适配

DNS-over-HTTP/TLS(DoH/DoT)是 Android 9 开始引入的功能,但直到 Android 14 才真正成为系统级标配。说白了,就是把 DNS 查询从明文改成加密传输,防止中间人篡改。

你想想看,如果 DNS 被劫持,你访问的可能是假网站。所以这个功能对安全性来说非常重要。

14.3.1 系统级 DoH/DoT 配置

Android 14 允许用户在网络设置中直接配置私有 DNS 模式:

  • 关闭:使用系统默认的 DNS(明文)
  • 自动:系统自动选择是否使用 DoT
  • 指定主机名:用户输入 DoT 服务器地址(如 dns.google)

作为应用开发者,我们需要注意什么呢?

14.3.2 应用层适配

如果你的应用需要自定义 DNS 解析(比如某些安全应用或 VPN 类应用),建议使用 Network API 来检测系统是否启用了私有 DNS:

// 检测系统是否启用了私有 DNS
val network = connectivityManager.activeNetwork
val linkProperties = connectivityManager.getLinkProperties(network)

// 检查是否有私有 DNS 服务器
val privateDnsServerName = linkProperties?.privateDnsServerName
if (privateDnsServerName != null) {
    // 系统已启用私有 DNS,使用系统 DNS 即可
    Log.d("DNS", "Private DNS enabled: $privateDnsServerName")
} else {
    // 系统未启用私有 DNS,应用可以自行实现 DoH/DoT
    // 但要注意不要与系统设置冲突
}

我的建议:如果你的应用实现了自己的 DNS 解析逻辑(比如使用 OkHttp 的 DNS 接口),一定要检查是否与系统私有 DNS 冲突。我曾经见过一个案例——某款广告拦截 App 强制使用自己的 DNS,结果和系统 DoT 冲突,导致部分网站无法访问。

14.3.3 DoH 与 DoT 的选择

这里简单说一下 DoH 和 DoT 的区别:

  • DoT(DNS-over-TLS):使用 853 端口,专用连接,性能更好
  • DoH(DNS-over-HTTP):使用 443 端口,与 HTTPS 流量混在一起,更难被封锁

Android 系统默认支持的是 DoT。如果你的应用需要 DoH,建议使用第三方库(如 okhttp-dnsoverhttps)自行实现。

14.4 知识体系总览

说了这么多,我画了一张图来总结本章的核心内容。你可以把它当作一个快速参考:

网络与连接兼容性适配知识体系 WLAN 直连 (P2P) 权限变化: • NEARBY_WIFI_DEVICES • ACCESS_FINE_LOCATION • CHANGE_WIFI_STATE 适配要点: • 兼容厂商差异 • 权限降级处理 • 扫描超时处理 蓝牙权限细化 三大权限: • BLUETOOTH_SCAN • BLUETOOTH_ADVERTISE • BLUETOOTH_CONNECT 适配要点: • 分组请求权限 • neverForLocation • 兼容旧版本 DNS-over-HTTP/TLS 两种协议: • DoT (853端口) • DoH (443端口) 适配要点: • 检测私有 DNS • 避免冲突 • 第三方库实现 核心原则:权限最小化 + 兼容性降级 + 安全加密

14.5 总结与建议

好了,我们来快速回顾一下本章的重点:

  • WLAN 直连:关注 NEARBY_WIFI_DEVICES 权限,注意厂商差异
  • 蓝牙权限:三个权限分开申请,别忘了 neverForLocation 标记
  • DoH/DoT:检测系统私有 DNS 配置,避免应用层冲突

我个人觉得,网络连接这块的适配其实不难,难的是想全各种边界情况。你想想看,用户可能从 Android 11 升级到 14,权限模型变了三次,你的应用能不能平滑过渡?

嗯,这里再分享一个我踩过的坑:有一次我适配蓝牙权限时,只考虑了 Android 12+ 的情况,结果在 Android 11 上直接崩溃了——因为 BLUETOOTH_SCAN 这个常量在旧版本不存在。所以一定要做好版本判断。

最后说一句:网络连接兼容性适配,说白了就是「权限 + 协议」两个维度。权限跟着系统走,协议跟着安全走。把这两条线理清楚,大部分问题都能迎刃而解。


公众号:蓝海资料掘金营,微信deep3321