Nearby Connections 重大变更:不再自动开启 Wi-Fi / 蓝牙,适配指南
7 月 20 日Android Developers Blog 发了一条 Nearby Connections API 的变更通知。到 2026 年底以后Nearby Connections 不会再帮 1P 和 3P 应用自动打开 Wi-Fi / Bluetooth。应用在调用发现、广播和连接前要自己确认无线开关状态如果用户关了开关就要让用户手动打开。这类变化看起来只影响一个 SDK 行为实际会改掉很多近场连接功能的失败路径。以前用户点了“搜索附近设备”SDK 可能在背后把需要的无线能力拉起来后面这个动作要回到应用自己的 UI 和状态机里。影响Nearby Connections 是 Google Play services 里的近场 P2P API用来做附近设备发现、连接和数据交换。它底层会用到 Bluetooth、BLE 和 Wi-Fi开发者不用自己拼这些无线协议。常见入口是两个一台设备调用startAdvertising()让自己可被发现另一台设备调用startDiscovery()搜索附近端点。找到端点以后再requestConnection()双方接受连接后用sendPayload()传 bytes、file 或 stream。代码大概是这样private const val SERVICE_IDcom.example.nearbyprivate val STRATEGYStrategy.P2P_POINT_TO_POINT fun startDiscovery(context: Context){val optionsDiscoveryOptions.Builder().setStrategy(STRATEGY).build()Nearby.getConnectionsClient(context).startDiscovery(SERVICE_ID, endpointDiscoveryCallback, options).addOnSuccessListener{// 开始发现附近设备}.addOnFailureListener{error -// 这里要展示明确失败原因而不是一直停在搜索中}}这段代码变化在调用它之前。以前很多项目把权限判断放在最前面权限过了就直接启动发现或广播。后面还要补一个无线开关判断否则用户关着 Wi-Fi 或 Bluetooth 时页面可能只会表现成“搜不到设备”。连接前检查状态检查不需要写得很复杂核心是把“权限没给”和“开关没开”分成两个 UI 状态。权限没给走运行时权限开关没开走系统设置面板或系统设置页。一个简单的状态对象可以这样写data class NearbyRadioState(val wifiEnabled: Boolean, val bluetoothEnabled: Boolean,){val disabledItems: ListStringget()buildList{if(!wifiEnabled)add(Wi-Fi)if(!bluetoothEnabled)add(Bluetooth)}}fun Context.nearbyRadioState(): NearbyRadioState{val wifiManagergetSystemService(WifiManager::class.java)val bluetoothManagergetSystemService(BluetoothManager::class.java)val adapterbluetoothManager.adapterreturnNearbyRadioState(wifiEnabledwifiManager.isWifiEnabled, bluetoothEnabledadapter?.isEnabledtrue,)}这里没有去调用setWifiEnabled(true)或BluetoothAdapter.enable()。原因也很直接面向新系统和新 target 的普通应用已经不能靠这两个 API 静默打开无线开关。WifiManager.setWifiEnabled()从 Android 10 开始对 target Q 及以上应用会失败并返回false。BluetoothAdapter.enable()从 Android 13 开始对 target TIRAMISU 及以上应用也会失败并返回false。设备管理器、Profile Owner、系统应用有豁免普通业务 App 不应该把它当兜底方案。引导用户打开Wi-Fi 这边可以优先用 Settings Panel。它是 Android 10 加的浮层设置入口Settings.Panel.ACTION_WIFI会展示包含 Wi-Fi 控件的系统面板用户处理完以后回到当前 App。Bluetooth 没有同样的通用 Panel 常量通常跳到蓝牙设置页fun Activity.openWifiSettings(){val intentif(Build.VERSION.SDK_INTBuild.VERSION_CODES.Q){Intent(Settings.Panel.ACTION_WIFI)}else{Intent(Settings.ACTION_WIFI_SETTINGS)}startActivity(intent)}fun Activity.openBluetoothSettings(){startActivity(Intent(Settings.ACTION_BLUETOOTH_SETTINGS))}页面文案不要写成“应用正在打开蓝牙”。更准确的表达是“需要打开 Bluetooth 才能搜索附近设备”然后给一个跳转设置的按钮。用户从设置页回来以后重新读取nearbyRadioState()状态满足再调用 Nearby Connections。如果两个开关都关着不建议连续弹两个系统页面。先在业务页把缺失项列出来让用户知道为什么不能继续。点“去设置”以后可以先处理 Wi-Fi再让用户处理 Bluetooth也可以放两个明确按钮避免用户在系统设置里来回找。权限别混在一起Nearby Connections 的权限声明这些年已经变得比较碎。老版本里会看到ACCESS_WIFI_STATE、CHANGE_WIFI_STATE、BLUETOOTH、BLUETOOTH_ADMINAndroid 12 以后又有BLUETOOTH_ADVERTISE、BLUETOOTH_CONNECT、BLUETOOTH_SCANAndroid 13 以后还会碰到NEARBY_WIFI_DEVICES。如果项目 target 到 Android 17并且使用WIFI_LAN文档里还列了ACCESS_LOCAL_NETWORK。这类权限要按项目的 SDK、策略和 payload 类型裁剪不能直接拿旧 Manifest 混过去。示例可以先写成这样再按项目实际入口收敛!-- Nearby Connections: legacy permissions --uses-permission android:nameandroid.permission.ACCESS_WIFI_STATEandroid:maxSdkVersion31/uses-permission android:nameandroid.permission.CHANGE_WIFI_STATEandroid:maxSdkVersion31/uses-permission android:nameandroid.permission.BLUETOOTHandroid:maxSdkVersion30/uses-permission android:nameandroid.permission.BLUETOOTH_ADMINandroid:maxSdkVersion30/!-- Android12 Bluetooth runtime permissions --uses-permission android:nameandroid.permission.BLUETOOTH_ADVERTISEandroid:minSdkVersion31/uses-permission android:nameandroid.permission.BLUETOOTH_CONNECTandroid:minSdkVersion31/uses-permission android:nameandroid.permission.BLUETOOTH_SCANandroid:minSdkVersion31/!-- Android13 nearby Wi-Fi permission --uses-permission android:nameandroid.permission.NEARBY_WIFI_DEVICES/这里要分清两件事权限允许 App 使用相关能力开关状态决定设备当前是否能执行对应无线动作。用户同意了蓝牙权限但系统蓝牙开关是关的Nearby Connections 仍然不能按预期发现和连接。失败状态这次变化最容易漏的是失败展示。很多“搜索附近设备”的页面只有 loading 和空列表用户看到的是一直转圈开发者日志里才有失败异常。无线开关不再自动打开以后这种 UI 会更容易出问题。我会把启动发现拆成三个判断运行时权限、无线开关、Nearby 调用结果。前两个是同步的业务状态最后一个才是 SDK 返回。fun startNearbyDiscovery(context: Context){if(!hasNearbyPermissions(context)){showPermissionRequest()return}val radioStatecontext.nearbyRadioState()if(radioState.disabledItems.isNotEmpty()){showRadioDisabled(radioState.disabledItems)return}Nearby.getConnectionsClient(context).startDiscovery(SERVICE_ID, endpointDiscoveryCallback, discoveryOptions).addOnFailureListener{error -showNearbyError(error)}}showRadioDisabled()里不要只给一句“连接失败”。更有用的是把缺失项写出来例如“打开 Wi-Fi 后再搜索附近设备”。用户打开设置返回 App 后页面重新检查状态如果状态还没变就继续停在引导页不要立刻进入搜索 loading。还有一个细节是恢复流程。Activity 回到前台时重新读一次无线状态连接页从RadioDisabled切到Ready后再启动发现。不要只依赖设置页返回结果因为系统设置页通常不会给业务 App 返回“用户确实打开了开关”的强语义结果。最后Nearby Connections 后面不会再替应用自动打开 Wi-Fi / Bluetooth。调用startAdvertising()或startDiscovery()前把运行时权限和无线开关分开检查缺开关时引导用户到系统设置回来后重新读状态。这个改动不需要重写 Nearby Connections 连接流程但会影响搜索页、配网页、面对面传输、本地多人游戏这类入口的失败处理。[#Android](javascript: [#NearbyConnections](javascript: [#GooglePlayServices](javascript: [#Android开发](javascript: