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 知识体系总览
说了这么多,我画了一张图来总结本章的核心内容。你可以把它当作一个快速参考:
14.5 总结与建议
好了,我们来快速回顾一下本章的重点:
- WLAN 直连:关注 NEARBY_WIFI_DEVICES 权限,注意厂商差异
- 蓝牙权限:三个权限分开申请,别忘了 neverForLocation 标记
- DoH/DoT:检测系统私有 DNS 配置,避免应用层冲突
我个人觉得,网络连接这块的适配其实不难,难的是想全各种边界情况。你想想看,用户可能从 Android 11 升级到 14,权限模型变了三次,你的应用能不能平滑过渡?
嗯,这里再分享一个我踩过的坑:有一次我适配蓝牙权限时,只考虑了 Android 12+ 的情况,结果在 Android 11 上直接崩溃了——因为 BLUETOOTH_SCAN 这个常量在旧版本不存在。所以一定要做好版本判断。
最后说一句:网络连接兼容性适配,说白了就是「权限 + 协议」两个维度。权限跟着系统走,协议跟着安全走。把这两条线理清楚,大部分问题都能迎刃而解。