Android 中运用 WI-FI 直连/P2P 完成高速数据传输
原作者:Tans5
一个大文件极速传输使用,支撑android端,以及mac,windows。大大解决了android手机和mac之间传输文件的繁琐问题。
0 简略介绍
就算做过好几年的 Android 开发老油条,有很多人都不知道 WI-FI 直连。简略来说便是能够将两台支撑 WI-FI 直连设备树立一条高速的通讯衔接,衔接树立后咱们就能够经过 TCP/UDP 协议来传输咱们想要的数据。相对于蓝牙的数据传输,WI-FI 直连的传输速度、距离、延迟和稳定性都大幅度优于蓝牙;相对于一般的局域网内的通讯速度和稳定性也是有优势,只是在某些比较好的局域网内才干够与 WI-FI 直连 比美。
WI-FI 直连一般用在需求传递很多数据的场景,比方大文件的共享、无人机图传等等场景。Android 从 4.0 版别就已经支撑 WI-FI 直连,基本便是有 WI-FI 的 Android 设备都支撑。条件要求特别低只需求两台手机翻开 WI-FI 就能创建衔接。(不需求手机接入 WLAN 哦)
给一个我自己开源的基于 WI-FI 直连的文件传输使用截图:
感兴趣的能够看看源码完成:tFileTransfer WI-FI 直连相关代码, 其间代码我用协程封装了一下。
Tips: 需求到达上面的传输速度,还得经过多线程创建多条衔接传输数据,才干到达网络带宽的上限。
1 权限增加
1.1 一般权限
// ...
<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.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
// ...
1.2 灵敏权限
灵敏权限在 Android 6 今后需在运行时动态再恳求一次,动态恳求权限的代码就不再贴了。
在 Android 13 及其今后的版别需求 NEARBY_WIFI_DEVICES 权限:
// ...
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="tiramisu" />
// ...
在 Android 12 及其以前的版别需求 ACCESS_FINE_LOCATION 权限:
// ...
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="32"/>
// ...
1.3 注意
在 Android 上要让 WI-FI 直连正常工作,一定得翻开 WI-FI 和 GPS 开关。
2 构建 WI-FI 直连衔接
2.1 预备工作
获取 WifiP2pManager 和 初始化 WifiP2pChannel:
val wifiP2pManager = context?.getSystemService(Context.WIFI_P2P_SERVICE) as? WifiP2pManager // 获取 WiFiP2pManager
val wifiChannel = wifiP2pManager?.initialize(requireActivity(), requireActivity().mainLooper, null) // 初始化 WifiChannel
动态注册 BroadcastReceiver 来监听后续操作中 WI-FI 直连中需求用到的各种状况:
val intentFilter = IntentFilter().apply {
addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION) // Wifi 直连可用状况改动
addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION) // Wifi 直连发现的设备改动
addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION) // Wifi 直连的衔接状况改动
}
requireActivity().registerReceiver(wifiReceiver, intentFilter)
private val wifiReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1)
if (state == WifiP2pManager.WIFI_P2P_STATE_DISABLED) {
AndroidLog.e(TAG, "Wifi p2p disabled.")
} else {
AndroidLog.d(TAG, "Wifi p2p enabled.")
}
}
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
val wifiDevicesList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, WifiP2pDeviceList::class.java)
} else {
intent.getParcelableExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST)
}
AndroidLog.d(TAG, "WIFI p2p devices: ${wifiDevicesList?.deviceList?.joinToString { "${it.deviceName} -> ${it.deviceAddress}" }}")
}
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
AndroidLog.d(TAG, "Connection state change.")
}
}
}
}
其间 WIFI_P2P_STATE_CHANGED_ACTION 表明 WI-FI 直连能够状况改动,比方说这时开关 WI-FI。
WIFI_P2P_PEERS_CHANGED_ACTION 表明发现的设备产生改动,后续会再讲到。
WIFI_P2P_CONNECTION_CHANGED_ACTION 表明衔接状况改动,后续会再讲到。
2.2 查询邻近设备
经过前面预备工作初始化的 WifiManager
和 WifiChannel
实例恳求查询邻近设备,一般能够经过用户触发查询,也能够敞开循环任务定时查询,取决于自己的需求。
wifiP2pManager.discoverPeers(wifiChannel, object : ActionListener {
override fun onSuccess() {
TODO("Not yet implemented")
}
override fun onFailure(p0: Int) {
TODO("Not yet implemented")
}
})
如果查询成功会触发能够触发预备工作中注册的播送监听,经过以下办法能够拿到对应的设备信息。
// ...
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
val wifiDevicesList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, WifiP2pDeviceList::class.java)
} else {
intent.getParcelableExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST)
}
// ...
}
// ...
查询设备成功后能够依据返回的成果,经过以下办法获取发现的设备的姓名和硬件地址。
for (d in wifiDevicesList?.deviceList ?: emptyList()) {
val deviceName = d.deviceName
val macAddress = d.deviceAddress
}
Tips: 从 Android 10 开端,Google 渐渐禁掉了能够定位用户设备的办法,类似于这儿的硬件地址,获取到的地址是假的,不过能够获取到其他设备的硬件地址。
查询成功后也能够手动触发查询设备。
wifiP2pManager.requestPeers(wifiChannel, object : PeerListListener {
override fun onPeersAvailable(p0: WifiP2pDeviceList?) {
TODO("Not yet implemented")
}
})
2.3 恳求与方针设备衔接
val config = WifiP2pConfig()
config.deviceAddress = data.macAddress
wifiP2pManager.connect(wifiChannel, config, object : ActionListener {
override fun onSuccess() {
TODO("Not yet implemented")
}
override fun onFailure(p0: Int) {
TODO("Not yet implemented")
}
})
其间的 MAC 地址,是在发现阶段获取到的。
相同的查询成功后也会触发上面动态注册的播送接收器。
// ....
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
// ....
}
// ...
经过以下办法查询衔接信息。
wifiP2pManager.requestConnectionInfo(wifiChannel) {
val ownerIpAddress = it.groupOwnerAddress
val isOwner = it.isGroupOwner
}
这个时分衔接就能够运用了,如果当时设备是 owner 那么,owner ip address 便是当时设置的 IP 地址,一般这时 owner 就能够敞开一个 TCP / UDP 服务,非 owner 的设备就能够衔接这个敞开的服务,这样一个网络衔接就树立完成了。能够传输你需求的数据了。
2.4 收回资源
如果不再运用,需求收回。
断开衔接,如果需求重新衔接其他设备,也能够调用这个办法把当时的衔接关闭。
wifiP2pManager.cancelConnect(wifiChannel, null)
wifiP2pManager.removeGroup(wifiChannel, null)
撤销注册播送。
requireActivity().unregisterReceiver(wifiReceiver)