在一次爬山途中,由于手机突然没有信号让我萌生了一个大胆的想法:能不能让手机之间直接连接并像微信一样通信呢?借离职后的自由时间,我决定挑战一下自己,开发一款小巧的设备间直连通信应用,在交互与功能层面尽量对齐微信,同时深度挖掘安卓系统底层能力,利用多种近场通信技术实现设备间的直接高效互联。
一、核心理念
- 去中心化: 摆脱服务器限制,探索设备间的直接互联。
- 多模态连接: 支持切换
Wi-Fi、Wi-Fi直连与蓝牙等连接方式。 - 技术全栈化: 深度集成硬件调度、数据加密、文件传输及音视频通信等核心模块。
二、技术栈
| 技术选型 | 说明 | |
|---|---|---|
| UI 框架 | Jetpack Compose | 构建响应式、现代化 UI |
| 依赖注入 | Hilt | 模块化代码解耦 |
| 页面导航 | Navigation3 | 类型安全的状态导航 |
| 数据持久化 | Room3, DataStore | 高效的本地数据缓存与配置管理 |
| 多媒体 | WebRTC, CameraX, MLKit, Coil3 | 提供实时音视频及媒体处理能力 |
| 地图服务 | AMap | 可插拔架构,支持标准版/轻量版 SDK 自由切换 |
| 加好友 | BLE | 低功耗蓝牙 |
| 设备发现 | NSD | 局域网内服务注册、发现、解析 |
| 通信 | TCP Socket / RfcommSocket | 设备间连接的底层方案 |
均采用主流技术栈及最新版本
三、功能介绍
mindmap
微信
聊天
联系人
我
设置
登录
mindmap
聊天
聊天列表
标为未读/已读
置顶聊天
不显示聊天
删除聊天
未读数显示
消息类型
文本
语音
表情
图片
视频
位置
文件
音乐
名片
输入框
文本输入
表情输入
全屏输入
草稿消息
通话
视频通话
语音通话
对消息的操作
复制
转发
删除
撤回
下载
多选(批量转发、删除、下载)
切换听筒/扬声器(语音消息)
状态显示
在线状态
加密状态
使用听筒播放语音
聊天信息
消息免打扰
置顶聊天
设置聊天背景
清空聊天记录
mindmap
联系人
联系人列表
新的朋友
自己
好友列表
好友数量
索引栏
长按设置朋友资料
联系人详情
头像(点击查看大图)
备注名
昵称
微信号
性别
个性签名
朋友资料
备注名
备忘
好友来源
添加时间
朋友设置
设置朋友资料
把他(她)推荐给朋友
加入黑名单
删除好友
mindmap
个人资料
头像
预览
修改
保存
名字
性别
微信号
我的二维码
二维码生成
扫一扫
换个样式
保存图片
个性签名
mindmap
通知设置
消息通知
语音和视频通话通知
通知显示内容
仅显示「你收到了1条消息」
显示朋友和群聊的名称
显示朋友、群聊名称及消息内容
系统通知设置入口
聊天界面中的新消息通知
声音控制
振动控制
消息提示音
跟随系统
内置提示音列表
点击支持试听
来电铃声
跟随系统
微信
其他内置铃声列表
点击支持试听
呼叫我时朋友也可听见我的来电铃声
mindmap
其他设置
界面与显示
深色模式
跟随系统
普通模式
深色模式
字体大小
通过滑块调节字体大小
实时预览
多语言
跟随系统
简体中文
English
朋友权限
加我为朋友时需要验证
通讯录黑名单
更多
系统权限状态显示
连接模式
WiFi 局域网
WiFi 直连
蓝牙
聊天
全局聊天背景设置
使用听筒播放语音消息
使用独立的发送按钮
端到端加密
聊天记录管理
清空全部聊天记录
退出登录
四、功能说明
1. 经典启动页
启动页之后会进入欢迎页面,可以修改app语言,点击开始使用将进入登录页面
2. 登录页面
- 也叫个人资料设置页面,设置头像和昵称后就能进入app。
- 点击确定后将会自动生成微信号和身份密钥。
- 私钥将被系统自动保存在硬件安全模块(TSS/SE)。公钥将随个人资料保存在DataStore。
3. 个人资料页面
- 头像可以预览、更换(裁剪)、保存到本地。
- 名字、性别、签名等支持修改。
- 微信号暂不支持修改。
二维码页面
- 支持扫一扫、更换样式、保存图片
4. 添加朋友页面
扫一扫
- 基于二维码
碰一碰
- 基于
NFC卡模拟/读卡
雷达
- 基于
UDP组播+轻量级HTTP头像服务器 - 设备需要在同一Wi-Fi下
加好友逻辑
其实无论基于何种添加方式,上层都只是获取对方的ID,底层都是走的 BLE(低功耗蓝牙)去拉取对方的资料,以及发送和处理好友申请。选择 BLE 的主要原因包括:
- 完全离线运行
- 可以不配对直接交换信息
- 可以在广播包中携带ID信息
- 设备发现速度和资料交换速度都还可以
密钥
在对方同意好友申请后,双方将交换个人资料,包括各自的公钥,这时是唯一获取对方公钥的时机。以后几乎所有的消息都要通过各自的私钥对数据包中几乎所有的字段+时间戳进行签名,然后对方通过保存的对方的公钥进行验签。这主要是为了解决信任问题,防止篡改和伪造。
好友资料更新机制
每次发送心跳包都会带一个基于时间戳的个人资料版本号,不是基于JSON,而是一个轻量级的二进制数据包,这样每次收到心跳,然后去和本地的好友的个人资料版本号进行比对,如果有更新就主动向对方发起拉取资料的请求。首次加好友后,一开始的头像其实是缩略图,因为蓝牙的带宽很小,但是如果他们在同一个Wi-Fi下,就会自动更新对方全量的资料。
5. 聊天页面
文本消息
- 支持表情
- 支持草稿消息
- 超过3行后可以进入全屏输入框
*输入框自带的语音输入由于没有接入服务器,并不好用,可以忽略~
语音消息
表情消息
图片/视频消息
- 支持实况图片
- 支持显示上传进度
- 大视频传输,支持双端进度显示,双端传输控制(暂停/继续/取消),且支持断点续传、秒传等
- 点击将进入app自带的媒体选择器
- 长按将进入系统自带的媒体选择器(部分手机自带的选择器可以选择擦除地理位置、相机参数等信息,以及获得更好的隐私保护)
相机
- 支持对焦
- 支持变焦
- 支持长按录像
- 支持切换摄像头
- 支持开关闪光灯
- 点击将进入app自带的相机
- 长按将进入系统自带的相机(可选拍照/拍视频)
位置消息
- 支持获取当前位置
- 支持搜索位置
- 支持位置快照
- 支持拖动地图自动查询
- 支持点击POI后聚焦及查询附近位置
- 支持调起导航软件(将显示已安装的地图app列表供用户选择)
名片消息
文件消息
小文件会直传,大文件会先协商再传输
- 支持大文件秒传
- 先计算文件的
SHA256哈希值 - 发送文件元数据给对方
- 对方查询该哈希值的文件是否存在
- 已存在则回复
AlreadyExists - 不存在则回复
ReadyToReceive
- 先计算文件的
- 支持分片传输+断点续传
- 不使用传统的保存若干个小分片文件,避免合并时的CPU与磁盘IO开销
- 预创建一个和目标文件大小一致的空文件,该操作仅占用元数据空间,有效避免了写入过程中的频繁磁盘空间申请与文件碎片化。
- 放弃传统的
FileOutputStream或RandomAccessFile阻塞式写入,采用FileChannel这种接近系统底层效率的API,利用FileChannel.position(offset)精确控制数据落盘位置,显著提升大文件在高并发写入时的吞吐量。
- 支持并发传输
- 在
write时加锁避免数据污染 - 每个分片数据包携带消息
id和offset,准确写入指定消息文件的指定位置 - 支持最多3个文件同时传输
- 蓝牙模式下控制为1个
- 在
- 实时发送进度显示
- 蓝牙模式下进度更新间隔会变小,以实现更平滑的更新
- 支持取消发送
- 支持打开文件
- 支持保存到本地
- 支持转发文件
- 转发的文件不重复保存,仅增加文件引用计数
发送app
- 支持选择当前手机已安装的app
- 同发送文件的逻辑
发送音乐
- 列表支持点击预览播放/暂停
- 音乐播放页面(参考的网易云音乐)
- 暂停和播放时,唱片的启停模拟物理世界的惯性
- 歌曲名支持循环滚动
通话
- 支持语音通话
- 支持视频通话
- 支持麦克风/摄像头/扬声器开关或切换等
- 支持切换小窗画面
- 小窗支持拖动与自动吸附
- 支持点击空白处收起控件
- 因为不用考虑带宽的问题,默认超清画质+CD级音质
- 支持完全无网环境接打电话,如:Wi-Fi直连、Wi-Fi热点模式
- 支持自定义来电铃声;支持播放对方设置的铃声
*暂未支持悬浮窗、锁屏接听等
*蓝牙模式下支持交换信令,但不支持通话(带宽小且无IP地址)
聊天信息&列表页面
- 消息免打扰
- 置顶聊天
- 设置聊天背景
- 清空聊天记录
- 置为未读/已读
- 隐藏聊天
6. 联系人页面
联系人列表&新的朋友
联系人详情
- 支持查看头像
- 昵称、性别、个性签名显示
- 支持设置备注名和备忘
- 好友来源&添加时间显示
联系人设置
- 加入黑名单
- 删除好友
7. 设置页面
通知设置
- 消息通知开关
- 通话通知开关
- 通知显示内容设置
- 系统通知设置入口
- 聊天界面的新消息通知设置
- 消息提示音设置
- 来电铃声设置
- 呼叫我时朋友也可听见我的来电铃声开关
界面与显示设置
- 深色模式
- 字体大小
- 多语言
朋友权限&更多设置
- 加我为朋友时需要验证
- 通讯录黑名单
- 系统权限获取状态
连接模式&聊天&聊天记录管理
Wi-Fi/Wi-Fi Direct/蓝牙3种连接方式动态切换- 设置全局聊天背景
- 使用听筒播放语音消息
- 使用独立的发送按钮
- 端到端加密:开启后将使用
AES加密所有的数据包,无论是文本数据还是文件分块,且密钥由每次握手时协商,私钥仅存于内存 - 清空全部聊天记录
8. 连接方式
无论底层基于何种连接方式,在上层都是一套ConnectionManager,抽象出了共同的方法:发送消息、接收消息和心跳机制等。他们的差异主要体现在发现设备、以及连接设备。像Wi-Fi和Wi-Fi Direct,他们可以基于共同的 TCPSocket,而蓝牙则是基于 RfcommSocket。
Wi-Fi:需要设备在同一Wi-Fi下
- 什么是 NSD?
NSD (Network Service Discovery) 是 Android 系统提供的原生服务,基于 mDNS (Multicast DNS) 协议实现。它允许应用在局域网内进行服务的注册 、发现 与 解析 ,无需手动输入 IP 地址即可实现设备互联。
- 局域网好友地址发现与连接机制
在 Wi-Fi 网络环境下,系统通过以下流程实现自动发现与通信:
-
服务广播与发现:后台持续通过 NSD 协议在局域网内广播自身服务信息(包含
wxid等标识符)。同时,监听网络中的其他广播包,解析出设备的wxid、Host及Port等关键元数据。 -
连接握手与验证:当发现的设备
wxid匹配好友列表时,系统自动发起 TCP 连接请求。 -
连接池管理与持久化:
- 连接池:建立成功的连接实例以
wxid为 Key 存入内存连接池。 - 持久化:同步将最新的连接拓扑信息(IP/Port)写入本地数据库,以便在 App 重启或网络波动后快速恢复连接。
- 连接池:建立成功的连接实例以
-
消息发送逻辑:发送消息时,系统优先从内存连接池检索对应的
Socket实例。若连接不存在或已失效,则利用数据库缓存的地址信息重新发起连接。
Wi-Fi直连:需要手动选择设备并配对,支持无网环境
-
协议原理:与传统 Wi-Fi 需要路由器作为中心节点不同,Wi-Fi Direct 允许设备间通过软件定义的“软 AP”直接建立连接。
- 角色协商 :连接时,两台设备会通过协议协商谁充当 GO (Group Owner) ,谁充当 GC (Group Client) 。GO 负责管理 DHCP 分配 IP,GC 像连接普通 Wi-Fi 一样接入。
- 与普通Wi-Fi模式下的通信逻辑基本共享。
-
技术优势
- 支持无网环境:完全脱离路由器限制,在户外、偏远地区或公共场所(禁用局域网通信时)仍可实现高速互联。
- 吞吐量极大:由于是点对点直连,减少了路由器中转的延迟和干扰。
可以看到Wi-Fi直连的传输速度是非常可观的
蓝牙:需要手动选择设备并配对,支持无网环境
-
采用
经典蓝牙 (RFCOMM) -
技术优势
- 稳定性较好
- 安全性较高
- 限制与痛点
- 传输速度慢
- 传输距离近
Wi-Fi热点模式
- 在户外环境,一个设备开热点,其他设备连接也是可以的,甚至可以打电话,和Wi-Fi直连类似。
五、源码&安装包
源码
安装包
注:可通过修改系统默认的省电策略为无限制来保持锁屏后的稳定连接。
六、结语
钟睒睒说:
我们要不断学习新的东西,打破自己原来知识结构的藩篱。
这个项目让我学到了很多东西,现在毫无保留的分享给大家,希望能帮到你~
完^_^