WiFi P2P(点对点)基础:WifiP2pManager介绍、发现对等设备、连接对等设备

各位同学,今天我们来聊聊WiFi P2P。说白了,就是让两台设备不经过路由器,直接通过WiFi连起来。你想想看,没有网线、没有热点,两台手机就能互相传文件、投屏、甚至联机打游戏——这就是P2P的魅力。

我在做项目时,经常遇到需要临时组网的场景。比如会议室里,领导说「把这份资料传到所有人手机上」,你总不能让大家一个个连热点吧?P2P就派上用场了。好,我们直接进入正题。

WifiP2pManager——P2P操作的总指挥

Android里操作P2P,核心就是WifiP2pManager这个类。它负责发现设备、连接设备、管理群组等所有操作。你可以把它理解成一个遥控器,按哪个键,设备就执行哪个动作。

获取它的方式很简单:

WifiP2pManager manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
WifiP2pManager.Channel channel = manager.initialize(this, getMainLooper(), null);

这里有个坑,我刚开始做的时候踩过——initialize()方法的第一个参数必须是ActivityService的上下文,不能是ApplicationContext。为什么?因为P2P需要绑定到具体的界面生命周期。你传个Application进去,回调可能收不到。

注意:别忘了在AndroidManifest.xml里声明权限: android.permission.ACCESS_WIFI_STATEandroid.permission.CHANGE_WIFI_STATEandroid.permission.INTERNET。 另外,Android 12以上还需要NEARBY_WIFI_DEVICES权限。

发现对等设备——discoverPeers

有了Manager,下一步就是发现周围有哪些设备支持P2P。调用discoverPeers()方法,系统就会开始扫描附近的P2P设备。

manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        // 发现过程启动成功,但此时还没拿到设备列表
    }

    @Override
    public void onFailure(int reason) {
        // 失败原因:WIFI_P2P_FAILURE、BUSY、ERROR等
    }
});

注意,onSuccess只表示「发现动作」启动了,并不代表已经找到了设备。真正的设备列表,需要通过广播接收器来获取。嗯,这里要重点说一下。

系统会发送一个WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION广播,你需要在广播接收器里调用requestPeers()来拿到实际的设备列表:

// 在广播接收器中
if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
    manager.requestPeers(channel, new WifiP2pManager.PeerListListener() {
        @Override
        public void onPeersAvailable(WifiP2pDeviceList peers) {
            // peers.getDeviceList() 就是附近所有P2P设备
            for (WifiP2pDevice device : peers.getDeviceList()) {
                Log.d("P2P", "发现设备: " + device.deviceName + " - " + device.deviceAddress);
            }
        }
    });
}

我曾经在一个项目里,发现设备死活扫描不到。查了半天,原来是手机WiFi没打开。嗯,这种低级错误,大家应该不会犯吧?

小技巧:如果扫描不到设备,先检查WiFi开关是否打开。另外,部分国产手机对P2P支持不完善,可能需要手动开启「WiFi直连」功能。

连接对等设备——connect

发现设备之后,下一步就是连接。调用connect()方法,传入一个WifiP2pConfig对象,指定你要连哪台设备。

WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = targetDevice.deviceAddress; // 目标设备的MAC地址
// 可选:设置WPS方式
config.wps.setup = WpsInfo.PBC; // 按键连接,也可以选PIN码

manager.connect(channel, config, new WifiP2pManager.ActionListener() {
    @Override
    public void onSuccess() {
        // 连接请求已发出,等待连接成功广播
    }

    @Override
    public void onFailure(int reason) {
        // 连接失败
    }
});

连接成功或失败,同样通过广播来通知。你需要监听WIFI_P2P_CONNECTION_CHANGED_ACTION

if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
    NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
    if (networkInfo.isConnected()) {
        // 连接成功!可以开始传输数据了
        manager.requestConnectionInfo(channel, new WifiP2pManager.ConnectionInfoListener() {
            @Override
            public void onConnectionInfoAvailable(WifiP2pInfo info) {
                // info.groupOwnerAddress 是群组所有者的IP地址
                // info.isGroupOwner 判断本机是否是群主
                // 根据角色决定是开Server还是Client
            }
        });
    }
}

这里有个关键点:P2P连接后,两台设备会形成一个群组。其中一台是「群主」(Group Owner),另一台是「客户端」。群主相当于一个小型热点,客户端连上来。IP地址方面,群主通常是192.168.49.1,客户端是192.168.49.x。

核心逻辑:连接成功后,你需要根据isGroupOwner的值来决定启动Socket服务器还是客户端。群主开ServerSocket监听,客户端主动连接群主的IP和端口。

知识体系总览

为了让你更直观地理解整个流程,我画了一张图:

WiFi P2P 工作流程 1. 初始化Manager getSystemService + initialize 2. 发现设备 discoverPeers + 广播接收 3. 选择设备 requestPeers 获取列表 4. 发起连接 connect + WPS配置 5. 监听连接结果 CONNECTION_CHANGED广播 6. 获取连接信息 requestConnectionInfo 7a. 群主:启动ServerSocket 等待客户端连接 7b. 客户端:连接群主 Socket(群主IP, 端口) 数据传输开始!

避坑指南

我做了好几个P2P项目,踩过的坑不少。这里列几个最常见的:

  • 设备发现超时:discoverPeers启动后,如果30秒内没有发现设备,系统会自动停止扫描。你需要重新调用一次。我曾经在循环里加了个定时器,每25秒重新发现一次。
  • 连接后无法通信:检查防火墙!部分手机自带的安全软件会拦截P2P的Socket连接。我遇到过小米手机默认拦截的情况,需要在安全中心里放行。
  • 群主切换问题:两台设备都支持P2P,但谁当群主是系统决定的。你不能强制指定。不过可以通过createGroup()来主动创建群组,让自己成为群主。
  • Android 10以上限制:从Android 10开始,P2P扫描需要位置权限。如果你发现扫描不到设备,先检查有没有申请ACCESS_FINE_LOCATION权限。
我的习惯:在开发阶段,我会把WifiP2pDevice的所有字段都打印到日志里,包括deviceNamedeviceAddressstatusprimaryDeviceType等。这样调试时一眼就能看出设备状态。

总结一下

WiFi P2P的核心就三步:初始化Manager、发现设备、连接设备。但真正用起来,广播接收器的注册、权限处理、Socket通信的细节,才是考验功力的地方。

我个人建议,初学者先写一个简单的Demo:两台手机互相发现,连接成功后,群主发一条「Hello」字符串给客户端。把这个跑通了,再考虑文件传输、实时通信等复杂场景。

好了,这一章就到这里。记住,P2P不是万能的,它适合近距离、小数据量的场景。如果你要传大文件,还是用蓝牙或者走服务器中转更靠谱。


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