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()方法的第一个参数必须是Activity或Service的上下文,不能是ApplicationContext。为什么?因为P2P需要绑定到具体的界面生命周期。你传个Application进去,回调可能收不到。
android.permission.ACCESS_WIFI_STATE、
android.permission.CHANGE_WIFI_STATE、
android.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没打开。嗯,这种低级错误,大家应该不会犯吧?
连接对等设备——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和端口。
知识体系总览
为了让你更直观地理解整个流程,我画了一张图:
避坑指南
我做了好几个P2P项目,踩过的坑不少。这里列几个最常见的:
- 设备发现超时:discoverPeers启动后,如果30秒内没有发现设备,系统会自动停止扫描。你需要重新调用一次。我曾经在循环里加了个定时器,每25秒重新发现一次。
- 连接后无法通信:检查防火墙!部分手机自带的安全软件会拦截P2P的Socket连接。我遇到过小米手机默认拦截的情况,需要在安全中心里放行。
- 群主切换问题:两台设备都支持P2P,但谁当群主是系统决定的。你不能强制指定。不过可以通过
createGroup()来主动创建群组,让自己成为群主。 - Android 10以上限制:从Android 10开始,P2P扫描需要位置权限。如果你发现扫描不到设备,先检查有没有申请
ACCESS_FINE_LOCATION权限。
WifiP2pDevice的所有字段都打印到日志里,包括deviceName、deviceAddress、status、primaryDeviceType等。这样调试时一眼就能看出设备状态。
总结一下
WiFi P2P的核心就三步:初始化Manager、发现设备、连接设备。但真正用起来,广播接收器的注册、权限处理、Socket通信的细节,才是考验功力的地方。
我个人建议,初学者先写一个简单的Demo:两台手机互相发现,连接成功后,群主发一条「Hello」字符串给客户端。把这个跑通了,再考虑文件传输、实时通信等复杂场景。
好了,这一章就到这里。记住,P2P不是万能的,它适合近距离、小数据量的场景。如果你要传大文件,还是用蓝牙或者走服务器中转更靠谱。
公众号:蓝海资料掘金营,微信deep3321