news 2026/9/7 7:30:06

Android Wi-Fi P2P官方Demo实战:从设备发现到Socket传文件全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android Wi-Fi P2P官方Demo实战:从设备发现到Socket传文件全解析

简介:Android官方Wifi P2P Demo用于帮助开发者理解并实现Wi-Fi Direct(Wi-Fi Peer-to-Peer)技术,适用于设备间直连、高速数据传输或流媒体共享等场景。压缩包共88个文件,包含7个Java源文件、14个XML资源文件、26个class文件及APK、JAR等编译产物,整体仅244KB,结构紧凑,便于对照学习。已有781人浏览学习。资源不只是一份示例代码,还附有详细的功能拆解,涵盖设备发现、群组创建与管理、连接建立、文件传输和状态更新等核心机制,并分析了WifiP2pManager等关键API的使用方法。通过研读源码与配套说明,开发者可以快速掌握Wi-Fi P2P的开发要点与注意事项,为文件共享、游戏联机、智能家居控制等应用场景打下基础。 不知道你有没有遇到过这种场景:两台手机之间要传一个几百MB的视频,蓝牙慢到能去泡杯咖啡,走社交软件又得绕服务器转一圈,而周围偏偏没有路由器也没有蜂窝信号。其实Android官方早就准备了一套方案——Wi-Fi P2P(也就是常说的Wi-Fi Direct),而最直观的入门教材,就是官方那个Wifi P2P Demo。这篇文章我会从这个官方Demo出发,结合我实际跑通、踩坑、再改造它的完整经历,把设备发现、连接建立、Socket传数据这条链路讲透,也顺带说说那些文档里没写、但真机会逼疯你的细节。

如果你是刚接触Android网络开发、或者准备做文件互传、离线局域网通信这类功能的开发者,这篇内容可以直接给你省掉至少一周的摸索时间。

1. 为什么非要用Wi-Fi P2P:没路由器也能组“局域网”的场景

1.1 什么是Wi-Fi P2P,和热点方式有什么本质区别

Wi-Fi P2P是Android从4.0(API 14)开始提供的原生点对点通信能力,它允许两台设备在没有无线接入点(AP)的情况下,通过Wi-Fi网卡直接建立连接。很多人第一反应是“这不就是开热点吗”,其实两者有本质区别。

开热点的方式是:一台设备变成软AP,另一台设备像连普通Wi-Fi一样去连接它。这种方式的缺点是明显的,热点设备要承担完整的AP功能,网卡工作模式切换慢、功耗高,而且对端需要手动输密码,连接体验非常糟糕。Wi-Fi P2P则是由系统底层协商出一个“组”(P2P Group),组里自动选出一台设备当Group Owner(简称GO),另一台当Client,整个过程对用户几乎透明,不需要输密码,也不需要手动切热点。

我当时之所以翻出官方Demo来研究,就是因为要在两个没有网络的环境下做设备间文件同步。最开始用的就是手机热点方案,结果被两个问题折腾得够呛,一个是华为手机开热点后自动关闭Wi-Fi导致无法同时上网,另一个是iOS设备连热点弹窗确认太慢。换成Wi-Fi P2P之后,这些问题基本都绕开了。

1.2 P2P、蓝牙、普通热点的横向对比

为了让你在选型时心里有数,我把我实测过的三种方案放在一起做了个对比:

项目Wi-Fi P2P蓝牙手机热点 + Socket
不需要路由器支持支持支持
传输速度实际可到10~50MB/s2~3MB/s封顶取决于软AP性能
是否需要输密码不需要需要配对确认需要输密码
功耗中等较高
连接建立时间1~3秒5秒以上15秒以上
底层机制系统协商GO/Client主从配对软AP + DHCP

从表格能看出来,P2P在速度、连接体验、功耗这三个维度上取得了相对均衡的优势。它尤其适合“短距离、大文件、无网络环境”的场景,比如会议室的临时文件分发、户外设备数据采集、两台执法记录仪之间的文件同步。

2. 跑通官方Demo前,先把这些前置条件准备好

2.1 硬件与工具准备

官方Demo的正式名称是WiFiDirectDemo,在Android开发者网站和GitHub的Android示例仓库里都能找到。下载之后用Android Studio直接打开就行,Java版本和Gradle版本按仓库说明配置即可。

硬件方面,别的都能凑合,唯独“两台真机”不能凑合。我见过太多人用模拟器跑这个Demo,跑起来之后设备列表永远是空的,因为模拟器的Wi-Fi网卡是虚拟的,根本不支持P2P发现。我自己最早也犯过这个错,浪费了半天时间。

两台真机最好是不同品牌,原因后面会讲,这里先留个悬念。系统版本建议一台Android 8~9、一台Android 10以上的组合,这样能同时覆盖新旧权限模型。

2.2 权限配置:三个容易被忽略的关键点

打开Demo的AndroidManifest.xml,可以看到它声明了这么几个权限:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

其中前四个属于普通权限,直接声明就行。但ACCESS_FINE_LOCATION是运行时权限,Android 6.0之后必须在代码里动态申请,只写manifest是不够的。这个权限的作用后来被很多人误解过,它并不是因为P2P功能本身需要定位,而是因为Wi-Fi扫描结果可能暴露设备的地理位置,所以系统强制要求先拿定位权限。

到了Android 13(API 33),Google又引入了NEARBY_WIFI_DEVICES这个运行时权限,专门用于管理附近Wi-Fi设备的连接。如果你的targetSdk已经到33以上,除了定位权限之外,还得把这个权限也加上并动态申请。

2.3 怎么判断Demo是否真的跑通了

很多新手把Demo装上之后,看到两台手机能互相发现就以为成功了,其实那只是第一步。我建议按这个清单逐步验证:

  1. 两台设备打开Wi-Fi,在系统设置中找到“Wi-Fi直连”入口,确认系统底层能互相发现。
  2. 进入App,点击搜索设备,列表出现对端设备的名称和MAC地址。
  3. 点击列表中的设备发起连接,对端收到邀请弹窗并接受。
  4. 连接成功后,两台设备都能看到对方的IP地址和“Group Owner”标识。
  5. 在Demo的测试界面发送一条消息,对端能收到。

只有走到第5步,才算完整跑通了官方Demo。

3. 官方Demo的核心链路拆解:发现、连接、传数据

3.1 五个广播Action是理解Demo的入口

整个官方Demo的骨架,是围绕WifiP2pManager和一组系统广播搭建起来的。WifiP2pManager是系统提供的P2P控制入口,所有操作比如搜索设备、发起连接、获取连接信息,都是通过这个类的异步方法触发的。

但这套API有个特点,它的方法调用结果和异步事件,很多不是通过回调直接返回的,而是通过广播通知上层。官方Demo里的WiFiDirectBroadcastReceiver就扮演了这个中间人角色,它监听以下几个关键Action:

Action常量含义Demo中的处理
WIFI_P2P_STATE_CHANGED_ACTIONP2P功能开关状态变化关闭时提示用户
WIFI_P2P_PEERS_CHANGED_ACTION附近设备列表变化调用requestPeers刷新列表
WIFI_P2P_CONNECTION_CHANGED_ACTION连接状态变化获取连接信息并更新UI
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION本机设备信息变化更新本机名称和状态

理解这五个广播,就等于拿到了读Demo代码的地图。WiFiDirectActivity负责把接收器注册到系统、把UI组件和WifiP2pManager串起来,DeviceListFragment负责显示设备列表,DeviceDetailFragment负责显示连接详情和发起传输操作,各司其职。

3.2 连接建立的完整时序:谁发起、谁当组长

把Demo跑起来之后,你可能会好奇一个问题:当两台手机建立P2P连接时,系统到底做了什么?我用自己的话把这条链路梳理一下:

  1. 发起方调用discoverPeers(),系统开始搜索周围的P2P设备。这个过程并不是持续广播,而是一次有限时长的扫描。
  2. 搜索到设备后,系统发出WIFI_P2P_PEERS_CHANGED_ACTION广播,App通过requestPeers()拿到设备列表。
  3. 用户点击某个设备,App调用connect()传入WifiP2pConfig,系统开始协商。
  4. 协商的关键点在于:系统会根据信号强度、设备能力、电池状态等因素,自动决定谁是GO。GO相当于一个小型软AP,Client则像终端一样连接它。
  5. 连接成功后,系统发出WIFI_P2P_CONNECTION_CHANGED_ACTION广播,App通过requestConnectionInfo()拿到WifiP2pInfo,里面有GO的IP地址和当前角色。

这里有一个概念容易混淆,GO和Client并不意味着“发起方一定是GO”。有时候发起连接的一方反而被协商成Client,所以在写代码时千万别写死“我是发起方所以我是GO”这种逻辑,一切以WifiP2pInfo.isGroupOwner字段为准。

3.3 传输通道不是自动的:ServerSocket端口协商

这是整个Demo里最反直觉的地方。P2P连接只是打通了网络链路,但这台设备上的某个App想和对端App通信,还需要自己建立Socket连接。换句话说,Wi-Fi P2P管的是“路”,不管“车上拉什么货”。

官方Demo的处理方式很经典:连接成功之后,GO角色的一方会在本机启动一个ServerSocket,监听一个固定端口(经典版本默认是8888),然后Client通过GO的IP地址和这个端口发起连接。GOaccept()到这个连接之后,并没有直接复用这条通道传数据,而是做了一次“二次建连”——再创建一个Socket,主动连回Client在8888端口监听的ServerSocket

为什么设计这么绕?我个人的理解是,Demo的传输逻辑里收发两个方向需要独立的InputStreamOutputStream,如果只建一条连接,收发可能相互阻塞,代码写起来也别扭。两条独立连接之后,每台设备各自持有一条Socket作为发送通道,一条作为接收通道,逻辑上清爽很多。

理解了这一点,你再看DeviceDetailFragment里的代码,会发现它的数据发送其实只需要很短的代码量,核心逻辑就是拿到WifiP2pInfo之后判断角色、开ServerSocket、然后Socket读写。

4. 真机实测才暴露的四个坑,附完整排查思路

4.1 权限不足不是“弹窗拒绝”,而是静默失败

这个坑我印象太深了。有一次我在一台Android 10的设备上跑Demo,点击搜索设备后,设备列表一直空白,但没有任何报错。日志里只出现了权限拒绝的警告,不仔细看根本发现不了。

排查链路是这样的:我先是确认了两台设备都在同一个Wi-Fi环境里,排除了网络问题;然后又去系统设置里手动打开Wi-Fi直连,发现系统层面可以互相发现,这就把问题定位到了App层面。最后我无意中瞥见Logcat里有一条SecurityException,才想起来ACCESS_FINE_LOCATION是运行时权限,Demo的旧代码没有处理Android 10之后的动态申请逻辑。

解决方式很朴素:在onCreate里调用requestPermissions()动态申请定位权限,Android 13以上同时申请NEARBY_WIFI_DEVICES。这里提醒一句,权限授予后,如果之前已经调用过discoverPeers(),一定要重新调用一次扫描,否则权限白给了。

4.2 广播接收器的生命周期绑错了,收不到任何事件

官方Demo的WiFiDirectBroadcastReceiver是在onCreate里注册的,但如果你照葫芦画瓢写到自己项目里,很可能遇到一个问题:App退到后台再回来,或者跳转了其他页面,广播再也收不到了。

更合理的做法是把注册和解绑放在onResumeonPause里。这不是Demo代码本身的问题,而是Activity生命周期和广播注册时机的经典矛盾。官方Demo因为只有一个Activity、没有复杂跳转,所以onCreate注册问题不大;但真实项目里页面一多,你不放在onResume里,就会出现“点击了搜索但列表死活不刷新”的诡异现象。

这个坑排查起来也比较有迷惑性,因为问题不在P2P本身,而在Activity生命周期管理。

4.3 连接成功但Socket连不上:端口和ROM的双重陷阱

连接状态已经变成“Connected”了,两台设备的WifiP2pInfo也都拿到了,但Socket就是连不上。这个坑我前前后后踩了两天,最后发现是由两个叠加因素导致的。

第一个因素是端口。Demo默认用8888端口,但这个端口在不少设备的系统服务里已经被占用了。我在某台设备上测试时,ServerSocket绑定8888直接抛了BindException,而Demo的代码没做失败提示,所以表现就是“看起来一切正常但传不了数据”。这个必须改成高位随机端口,或者至少绑定前先检查端口可用性。

第二个因素更隐蔽,部分国内ROM(我遇到的尤其是某些华为和荣耀机型)对后台Socket连接有严格限制,特别是热点、P2P相关的传输容易被系统优化策略杀掉。表现是:App在前台时传输正常,锁屏或者切后台之后再传,连接立刻断开。解决办法是加前台服务保活,并且提示用户关闭对该应用的电池优化。

4.4 设备互相发现不了的排查清单

两台设备打开Demo之后,列表空空如也,这个问题的原因可能很多,我整理了一份排查顺序,每次遇到都按这个顺序走,能省下不少时间:

序号检查项操作建议
1两台设备的Wi-Fi是否开启移动数据开着不代表Wi-Fi开着
2定位权限是否已授予重新调用一次discoverPeers
3系统自带Wi-Fi直连能否发现能则说明P2P底层正常,问题在App
4是否处于同一普通AP覆盖范围复杂网络环境下发现会受影响
5重启其中一台的Wi-Fi清除P2P状态机的残留状态
6是否有一台设备已经连接了其他P2P组一台设备同一时间只能加入一个组

还有一个经验是,两台同品牌、同型号的设备反而更容易出现发现不了的问题,因为系统有时会缓存之前的P2P协商状态,导致双方都在等对方“先开口”。跨品牌设备踩中这个概率要小很多。

5. 从Demo到能上线的项目,还差这几步改造

5.1 Demo到底缺了什么

官方Demo的定位是教学样例,它把P2P的核心能力串起来了,但离一个能用的产品还很远。我盘了一下,主要缺这几块:

一是没有文件传输的完整实现。严格说Demo只是建立连接后发送一条短文本消息,实际项目里传的是大文件,你要自己处理分包、校验、进度回调、断点续传。

二是配对体验差。Demo需要手动点击设备列表再弹窗确认,如果在户外场景让一个不太懂技术的人操作,基本劝退。商用解决方案大多用二维码或者碰一碰的方式来简化配对。

三是稳定性不足。P2P连接在弱信号和系统内存紧张时非常容易掉线,Demo没有任何重连机制。

5.2 稳定传输和功耗控制的改造

传大文件时,我建议应用层做两层处理:传输层用分块+CRC校验,一个块传输失败就重传这一块,而不是整个文件重新来;协议层用JSON定义消息头,包含文件名、总大小、块序号、校验值,这样对端能清晰判断数据完整性。

功耗方面,P2P虽然比热点模式好,但网卡一直全速收发数据时电量掉得也很快。建议在传输完成或空闲超过一定时间后主动调用removeGroup()断开P2P连接,别让它一直保持。另外,发送大文件时可以考虑把屏幕熄灭时间调短,减少亮屏耗电,这和保活逻辑并不冲突。

配对体验上,Android 10以上提供了WifiP2pConfig.toXml()fromXml()方法,可以把连接参数打包成XML字符串,再用二维码或者NFC触碰传递过去,对端解析后直接发起连接。我试过用ZXing把XML编码成二维码,扫码到解析再到连接成功,整个过程3秒左右,体验比手动选设备好很多。

5.3 选型建议:P2P不是万能方案,但用对场景很香

最后结合我的实际体会给你一些选型建议。如果只是两台设备在局域网内传文件,其实用普通Socket连接就好,没必要上P2P,因为P2P协商本身有延迟,而且对系统版本的兼容性要求高。

如果场景是“多台设备无网络环境下的文件分发”,比如野外勘察、临时会议,那P2P就是非常合适的选择。它比热点方案省去配网步骤,比蓝牙方案快一个数量级。但要注意P2P组内的设备数量限制,GO承载多个Client时性能压力会上升,跨厂商设备混组时也可能出现兼容性问题。

最后说一个我自己的深有体会的点:用这个Demo做演示时一定要准备两台真机,并且电量都充足。P2P调试过程里最耽误时间的往往不是代码逻辑,而是环境因素,权限、系统版本、厂商ROM都可能带来意外表现。如果你只是想先把功能跑通,别急着改代码,先去系统设置里把Wi-Fi直连功能手工验证一遍,底层通不通先确认好,再回到App层调试。这个顺序能帮你避开至少一半的坑。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 7:29:21

软考高级系统规划与管理师冲刺:IT服务管理与8小时背诵路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 7:28:23

OpenAI兼容多模型统一网关:架构设计、部署与运维实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 7:27:25

Ryzen 7 7800X3D + RTX 5070 游戏主机装机实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 7:26:43

GPS数据解析实战:NMEA 0183协议与C语言解析器实现

简介&#xff1a;一套完整的GPS数据解析C程序源码包&#xff0c;面向嵌入式开发者和单片机爱好者&#xff0c;解决GPS模块NMEA报文解析与12864液晶屏实时显示经纬度的问题。资源共23个文件&#xff0c;以.C源码、.H头文件、.OBJ目标文件和.LST列表文件为主&#xff0c;压缩包约…

作者头像 李华
网站建设 2026/9/7 7:26:25

第5章 智能元素理论

第5章 智能元素理论&#x1f4c5; 2026年09月06日&#x1f464; 东塬一老翁&#x1f4c2; 第二篇 智能的结构基础第5章 智能元素理论5.1 智能元素定义**智能元素&#xff08;Intelligence Element&#xff09;**是构成智能结构、智能能力、智能机制和智能行为的基本结构单元…

作者头像 李华
网站建设 2026/9/7 7:25:38

基于IIO子系统的嵌入式Linux频谱示波器实现与调优

简介&#xff1a;这份资源是一款面向Linux平台的频谱示波器软件&#xff0c;基于C与GTK图形库开发&#xff0c;并依托Linux内核的IIO框架与信号采集设备通信&#xff0c;适用于电子工程、通信技术及信号处理等领域的研发与调试场景。包体约45.43MB&#xff0c;共776个文件&…

作者头像 李华