16、WiFi Aware(邻居感知网络):WifiAwareManager介绍、发布与订阅服务、建立对等连接(Android 8+)

WiFi Aware,也叫邻居感知网络(NAN)。说实话,我第一次听到这个名字时,觉得它像某种科幻电影里的技术。但实际用起来,你会发现它解决了一个很实在的问题:设备之间怎么快速发现彼此,并且直接通信,不需要经过路由器或热点。

Android 8(API 26)开始,Google 正式把这个能力开放给了开发者。我当年在做一个线下社交 App 时,就踩过不少坑。今天我把这些经验整理出来,希望能帮你少走弯路。

什么是 WiFi Aware?

简单来说,WiFi Aware 让设备在不开热点、不连路由器的情况下,就能发现周围的其他设备,并建立连接。它工作在 2.4GHz 和 5GHz 频段,功耗比传统 WiFi 扫描低得多。

你可能会问:「这不就是蓝牙吗?」嗯,有点像,但 WiFi Aware 的传输速率更高,延迟更低,而且不需要配对过程。说白了,它更适合传输大文件或实时数据。

核心特点:

  • 无需接入点(AP),设备间直接通信
  • 低功耗发现,适合后台运行
  • 支持 1 对 1 和 1 对多通信
  • 基于 WiFi 的较高带宽

WifiAwareManager 介绍

WifiAwareManager 是 Android 提供的核心 API。要使用它,你得先获取系统服务:

WifiAwareManager awareManager = 
    (WifiAwareManager) context.getSystemService(Context.WIFI_AWARE_SERVICE);

if (awareManager == null || !awareManager.isAvailable()) {
    // 设备不支持 WiFi Aware
    return;
}

我个人习惯在 Application 初始化时就检查一次 availability。因为有些设备虽然跑着 Android 8+,但硬件不支持 WiFi Aware。提前检查可以避免运行时崩溃。

注意:WiFi Aware 需要硬件支持。不是所有 Android 8+ 设备都能用。建议在功能入口处做 availability 检查,并给出友好提示。

发布与订阅服务

WiFi Aware 的核心机制就是「发布-订阅」模式。一个设备发布服务,另一个设备订阅服务。当两者匹配时,系统会通知双方。

发布服务(Publish)

发布方需要创建一个 PublishConfig,指定服务名称和匹配规则:

PublishConfig config = new PublishConfig.Builder()
    .setServiceName("com.example.my_aware_service")
    .setMatchFilter(Arrays.asList(
        new byte[]{0x01, 0x02},
        new byte[]{0x03, 0x04}
    ))
    .build();

awareManager.publish(config, new PublishDiscoverySessionCallback() {
    @Override
    public void onPublishStarted(PublishDiscoverySession session) {
        // 发布成功,可以开始等待订阅者
    }

    @Override
    public void onServiceDiscovered(PeerHandle peerHandle, 
                                    byte[] serviceSpecificInfo, 
                                    List<byte[]> matchFilter) {
        // 有订阅者匹配到了我们的服务
    }
}, null);

这里有个坑:setMatchFilter 的参数是 byte 数组列表。我曾经传了空列表,结果发现所有订阅者都能匹配到我的服务,导致连接混乱。后来才明白,空列表表示「匹配所有」,而不是「不匹配」。

订阅服务(Subscribe)

订阅方类似,但用的是 SubscribeConfig

SubscribeConfig config = new SubscribeConfig.Builder()
    .setServiceName("com.example.my_aware_service")
    .setMatchFilter(Arrays.asList(
        new byte[]{0x01, 0x02},
        new byte[]{0x03, 0x04}
    ))
    .build();

awareManager.subscribe(config, new SubscribeDiscoverySessionCallback() {
    @Override
    public void onSubscribeStarted(SubscribeDiscoverySession session) {
        // 订阅成功
    }

    @Override
    public void onServiceDiscovered(PeerHandle peerHandle, 
                                    byte[] serviceSpecificInfo, 
                                    List<byte[]> matchFilter) {
        // 发现了匹配的发布者
    }
}, null);

小技巧:服务名称建议用反向域名格式,避免与其他应用冲突。匹配过滤器可以传递自定义数据,比如版本号或设备类型,这样能实现更精细的匹配逻辑。

建立对等连接

发现对方后,下一步就是建立连接。WiFi Aware 使用 WifiAwareSession 来管理连接。发布方和订阅方都可以发起连接请求。

我一般让发布方主动发起连接,这样逻辑更清晰:

// 在 onServiceDiscovered 回调中
session.createNetworkSpecifier(peerHandle, new int[]{0}, new WifiAwareNetworkSpecifier.Builder()
    .setPskPassphrase("my_shared_secret")
    .build());

// 然后通过 WifiNetworkSpecifier 建立 Socket 连接
WifiNetworkSpecifier networkSpecifier = new WifiNetworkSpecifier.Builder()
    .setWifiAwareNetworkSpecifier(awareNetworkSpecifier)
    .build();

NetworkRequest networkRequest = new NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
    .setNetworkSpecifier(networkSpecifier)
    .build();

connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback() {
    @Override
    public void onAvailable(Network network) {
        // 网络可用,可以创建 Socket 了
    }
});

嗯,这里要注意:createNetworkSpecifier 的第二个参数是 int[],表示支持的频段。0 表示 2.4GHz,1 表示 5GHz。我建议传 new int[]{0, 1},让系统自动选择最优频段。

避坑指南:我曾经在 onAvailable 回调里直接创建 Socket,结果发现连接超时。后来排查发现,网络虽然可用,但还需要等待几毫秒让路由表更新。建议加一个短暂延迟(比如 100ms)再创建 Socket。

知识体系总览

下面这张图总结了 WiFi Aware 的核心流程:

WiFi Aware 核心流程 设备A(发布者) 设备B(订阅者) ① 发布服务 等待订阅者匹配 扫描发现服务 ② 匹配成功 ③ 建立对等连接 ④ 数据传输(Socket)

生命周期管理

WiFi Aware 的 session 和 callback 都需要妥善管理。我建议在 Activity 或 Fragment 的 onStop 中关闭 session:

@Override
protected void onStop() {
    super.onStop();
    if (awareSession != null) {
        awareSession.close();
        awareSession = null;
    }
    if (networkCallback != null) {
        connectivityManager.unregisterNetworkCallback(networkCallback);
    }
}

为什么?因为 WiFi Aware session 会占用系统资源。如果不及时释放,可能导致其他应用无法使用 WiFi Aware。我曾经在测试时忘了关闭 session,结果手机重启后才恢复正常。

建议:使用 LifecycleObserver 自动管理 session 生命周期,避免手动管理带来的遗漏。

实际项目中的注意事项

场景 建议
设备发现 设置合理的超时时间(默认 120 秒),避免无限等待
数据传输 使用 TCP 而非 UDP,WiFi Aware 的丢包率在复杂环境下较高
多设备 每个 peer 有唯一的 PeerHandle,注意不要混淆
安全 使用 PSK 加密,不要传输明文数据

最后说一句:WiFi Aware 是个好东西,但生态还不够成熟。如果你在项目中遇到奇怪的问题,不妨先检查一下设备兼容性。有时候不是代码的问题,是硬件不支持。


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