基于Android的文件传输系统
开发步骤
- 在 AndroidManifest 中声明相关权限(网络和文件读写权限)
- 获取 WifiP2pManager ,注册相关广播监听Wifi直连的状态变化
- 指定某一台设备为服务器(用来接收文件),创建群组并作为群主存在,在指定端口监听客户端的连接请求,等待客户端发起连接请求以及文件传输请求
客户端(用来发送文件)主动搜索附近的设备,加入到服务器创建的群组,获取服务器的 IP 地址,向其发起文件传输请求
- 校验文件完整性
设置Android权限
Wifi P2P 技术并不会访问网络,但由于会使用到 Java Socket,所以需要申请网络权限。此外,由于是要实现文件互传,所以也需要申请SD卡读写权限。
<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" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
注册广播
与 Wifi P2P 相关的广播有以下几个:
- WIFI_P2P_STATE_CHANGED_ACTION( 用于指示 Wifi P2P 是否可用 )
- WIFI_P2P_PEERS_CHANGED_ACTION( 对等节点列表发生了变化 )
- WIFI_P2P_CONNECTION_CHANGED_ACTION( Wifi P2P 的连接状态发生了改变 )
- WIFI_P2P_THIS_DEVICE_CHANGED_ACTION( 本设备的设备信息发生了变化 )
当接收到这几个广播时,我们都需要到 WifiP2pManager (对等网络管理器)来进行相应的信息请求,此外还需要用到 Channel 对象作为请求参数
mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mWifiP2pManager.initialize(this, getMainLooper(), this);
当收到 WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION 广播时,可以判断当前 Wifi P2P是否可用
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
mDirectActionListener.wifiP2pEnabled(true);
} else {
mDirectActionListener.wifiP2pEnabled(false);
}
当收到 WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION 广播时,意味设备周围的可用设备列表发生了变化,可以通过 requestPeers 方法得到可用的设备列表,之后就可以选择当中的某一个设备进行连接操作
mWifiP2pManager.requestPeers(mChannel, new WifiP2pManager.PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
mDirectActionListener.onPeersAvailable(peers.getDeviceList());
}
});
当收到 WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION 广播时,意味着 Wifi P2P 的连接状态发生了变化,可能是连接到了某设备,或者是与某设备断开了连接
NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
mWifiP2pManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
mDirectActionListener.onConnectionInfoAvailable(info);
}
});
Log.e(TAG, "已连接p2p设备");
} else {
mDirectActionListener.onDisconnection();
Log.e(TAG, "与p2p设备已断开连接");
}
如果是与某设备连接上了,则可以通过 requestConnectionInfo 方法获取到连接信息
当收到 WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 广播时,则可以获取到本设备变化后的设备信息
(WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)
可以看出 Wifi P2P 的接口高度异步化,到现在已经用到了三个系统的回调函数,一个用于 WifiP2pManager 的初始化,两个用于在广播中异步请求数据,为了简化操作,此处统一使用一个自定义的回调函数,方法含义与系统的回调函数一致
public interface DirectActionListener extends WifiP2pManager.ChannelListener {
void wifiP2pEnabled(boolean enabled);
void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo);
void onDisconnection();
void onSelfDeviceAvailable(WifiP2pDevice wifiP2pDevice);
void onPeersAvailable(Collection<WifiP2pDevice> wifiP2pDeviceList);
}
//所以,整个广播接收器使用到的所有代码是:
public class DirectBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "DirectBroadcastReceiver";
private WifiP2pManager mWifiP2pManager;
private WifiP2pManager.Channel mChannel;
private DirectActionListener mDirectActionListener;
public DirectBroadcastReceiver(WifiP2pManager wifiP2pManager, WifiP2pManager.Channel channel, DirectActionListener directActionListener) {
mWifiP2pManager = wifiP2pManager;
mChannel = channel;
mDirectActionListener = directActionListener;
}
public static IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
return intentFilter;
}
@Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG, "接收到广播: " + intent.getAction());
if (!TextUtils.isEmpty(intent.getAction())) {
switch (intent.getAction()) {
// 用于指示 Wifi P2P 是否可用
case WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION: {
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
mDirectActionListener.wifiP2pEnabled(true);
} else {
mDirectActionListener.wifiP2pEnabled(false);
List<WifiP2pDevice> wifiP2pDeviceList = new ArrayList<>();
mDirectActionListener.onPeersAvailable(wifiP2pDeviceList);
}
break;
}
// 对等节点列表发生了变化
case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION: {
mWifiP2pManager.requestPeers(mChannel, new WifiP2pManager.PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
mDirectActionListener.onPeersAvailable(peers.getDeviceList());
}
});
break;
}
// Wifi P2P 的连接状态发生了改变
case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION: {
NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
mWifiP2pManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
mDirectActionListener.onConnectionInfoAvailable(info);
}
});
Log.e(TAG, "已连接p2p设备");
} else {
mDirectActionListener.onDisconnection();
Log.e(TAG, "与p2p设备已断开连接");
}
break;
}
//本设备的设备信息发生了变化
case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION: {
mDirectActionListener.onSelfDeviceAvailable((WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
break;
}
}
}
}
}
未完待续.....................................
效果展示
[图片上传失败...(image-89ef4a-1604286797706)]
[图片上传失败...(image-d1227e-1604286797706)]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。