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。

代码大概是这样:

bash 复制代码
private const val SERVICE_ID = "com.example.nearby"
private val STRATEGY = Strategy.P2P_POINT_TO_POINT

fun startDiscovery(context: Context) {
    val options = DiscoveryOptions.Builder()
        .setStrategy(STRATEGY)
        .build()

    Nearby.getConnectionsClient(context)
        .startDiscovery(SERVICE_ID, endpointDiscoveryCallback, options)
        .addOnSuccessListener {
            // 开始发现附近设备
        }
        .addOnFailureListener { error ->
            // 这里要展示明确失败原因,而不是一直停在搜索中
        }
}

这段代码变化在调用它之前。以前很多项目把权限判断放在最前面,权限过了就直接启动发现或广播。后面还要补一个无线开关判断,否则用户关着 Wi-Fi 或 Bluetooth 时,页面可能只会表现成"搜不到设备"。

连接前检查

状态检查不需要写得很复杂,核心是把"权限没给"和"开关没开"分成两个 UI 状态。权限没给,走运行时权限;开关没开,走系统设置面板或系统设置页。

一个简单的状态对象可以这样写:

bash 复制代码
data class NearbyRadioState(
    val wifiEnabled: Boolean,
    val bluetoothEnabled: Boolean,
) {
    val disabledItems: List<String>
        get() = buildList {
            if (!wifiEnabled) add("Wi-Fi")
            if (!bluetoothEnabled) add("Bluetooth")
        }
}

fun Context.nearbyRadioState(): NearbyRadioState {
    val wifiManager = getSystemService(WifiManager::class.java)
    val bluetoothManager = getSystemService(BluetoothManager::class.java)
    val adapter = bluetoothManager.adapter

    return NearbyRadioState(
        wifiEnabled = wifiManager.isWifiEnabled,
        bluetoothEnabled = adapter?.isEnabled == true,
    )
}

这里没有去调用 setWifiEnabled(true)BluetoothAdapter.enable()。原因也很直接:面向新系统和新 target 的普通应用,已经不能靠这两个 API 静默打开无线开关。

WifiManager.setWifiEnabled() 从 Android 10 开始对 target Q 及以上应用会失败并返回 falseBluetoothAdapter.enable() 从 Android 13 开始对 target TIRAMISU 及以上应用也会失败并返回 false。设备管理器、Profile Owner、系统应用有豁免,普通业务 App 不应该把它当兜底方案。

引导用户打开

Wi-Fi 这边可以优先用 Settings Panel。它是 Android 10 加的浮层设置入口,Settings.Panel.ACTION_WIFI 会展示包含 Wi-Fi 控件的系统面板,用户处理完以后回到当前 App。

Bluetooth 没有同样的通用 Panel 常量,通常跳到蓝牙设置页:

bash 复制代码
fun Activity.openWifiSettings() {
    val intent = if (Build.VERSION.SDK_INT >= Build.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_STATECHANGE_WIFI_STATEBLUETOOTHBLUETOOTH_ADMIN,Android 12 以后又有 BLUETOOTH_ADVERTISEBLUETOOTH_CONNECTBLUETOOTH_SCAN,Android 13 以后还会碰到 NEARBY_WIFI_DEVICES

如果项目 target 到 Android 17,并且使用 WIFI_LAN,文档里还列了 ACCESS_LOCAL_NETWORK。这类权限要按项目的 SDK、策略和 payload 类型裁剪,不能直接拿旧 Manifest 混过去。

示例可以先写成这样,再按项目实际入口收敛:

bash 复制代码
<!-- Nearby Connections: legacy permissions -->
<uses-permission
    android:name="android.permission.ACCESS_WIFI_STATE"
    android:maxSdkVersion="31" />
<uses-permission
    android:name="android.permission.CHANGE_WIFI_STATE"
    android:maxSdkVersion="31" />
<uses-permission
    android:name="android.permission.BLUETOOTH"
    android:maxSdkVersion="30" />
<uses-permission
    android:name="android.permission.BLUETOOTH_ADMIN"
    android:maxSdkVersion="30" />

<!-- Android 12+ Bluetooth runtime permissions -->
<uses-permission
    android:name="android.permission.BLUETOOTH_ADVERTISE"
    android:minSdkVersion="31" />
<uses-permission
    android:name="android.permission.BLUETOOTH_CONNECT"
    android:minSdkVersion="31" />
<uses-permission
    android:name="android.permission.BLUETOOTH_SCAN"
    android:minSdkVersion="31" />

<!-- Android 13+ nearby Wi-Fi permission -->
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />

这里要分清两件事:权限允许 App 使用相关能力,开关状态决定设备当前是否能执行对应无线动作。用户同意了蓝牙权限,但系统蓝牙开关是关的,Nearby Connections 仍然不能按预期发现和连接。

失败状态

这次变化最容易漏的是失败展示。很多"搜索附近设备"的页面只有 loading 和空列表,用户看到的是一直转圈,开发者日志里才有失败异常。无线开关不再自动打开以后,这种 UI 会更容易出问题。

我会把启动发现拆成三个判断:运行时权限、无线开关、Nearby 调用结果。前两个是同步的业务状态,最后一个才是 SDK 返回。

bash 复制代码
fun startNearbyDiscovery(context: Context) {
    if (!hasNearbyPermissions(context)) {
        showPermissionRequest()
        return
    }

    val radioState = context.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 #NearbyConnections #GooglePlayServices #Android开发

相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui