「Android」WiFiP2P入门
使用 WiFi 直连 (WiFiP2P) 技术,可以让具备相应硬件的 Android 4.0(API 级别 14)或更高版本设备在没有中间接入点的情况下,通过 WiFi 进行直接互联。使用这些 API,可以实现支持 WiFi P2P 的设备间相互发现和连接,从而获得比蓝牙连接更远距离的高速连接通信效果。
为了实现一个基础的WiFiP2P,大致分为如下部分:
- 权限申请
- 初始化WiFiP2P的相关对象
- 定义监听WiFiP2P的广播接收器
- 连接设备
关于WiFiP2P中的群组,大致分为如下部分:
- 创建群组
- 连接群组
- 移除群组
权限申请
首先,在AndroidManifest.xml中,对WiFi相关权限进行静态申请:
若后续还需要读写权限则添加:
然后,在 Android 6.0 及更高版本中,部分危险权限(Dangerous Permissions)权限需要在运行时请求用户批准(动态申请):
private void checkPermission() {
String[] permissions = new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_FINE_LOCATION
};
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, permission + " granted.");
} else {
ActivityCompat.requestPermissions(this, permissions, 0);
Log.w(TAG, permission + " not granted.");
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 0) {
for (int result : grantResults) {
if (result == PackageManager.PERMISSION_GRANTED) {
continue;
} else {
Toast.makeText(this, "权限未获取", Toast.LENGTH_SHORT).show();
}
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
初始化
首先,需要创建:
- WifiP2pManager 对象
- WifiP2pManager.Channel 对象
- WiFiDirectBroadcastReceiver 对象(稍后介绍该广播接收器的定义)
- IntentFilter 对象
private WifiP2pManager mManager;
private WifiP2pManager.Channel mChannel;
private WiFiDirectBroadcastReceiver mReceiver;
private IntentFilter mIntentFilter;
private void initWifip2pHelper() {
// 创建 WifiP2pManager 对象
mManager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE);
// 创建 WifiP2pManager.Channel 对象
mChannel = mManager.initialize(this, Looper.getMainLooper(), new WifiP2pManager.ChannelListener() {
@Override
public void onChannelDisconnected() {
Log.i(TAG, "onChannelDisconnected: ");
}
});
// 创建 WiFiDirectBroadcastReceiver 对象
mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
// 创建 IntentFilter 对象
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
}
其中, WiFiDirectBroadcastReceiver 对象想要监听的广播,与 IntentFilter 对象添加的action相同。
然后,在 Activity 的onResume()方法中注册广播接收器,在 Activity 的onPause()方法中取消注册该广播接收器:
/* register the broadcast receiver with the intent values to be matched */
@Override
protected void onResume() {
super.onResume();
registerReceiver(mReceiver, mIntentFilter);
}
/* unregister the broadcast receiver */
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mReceiver);
}
定义监听WiFiP2P的广播接收器
监听WiFiP2P的广播接收器 WiFiDirectBroadcastReceiver 类具体定义如下:
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "WiFiDirectBroadcastReceiver";
private WifiP2pManager mManager;
private WifiP2pManager.Channel mChannel;
private Wifip2pActivity mActivity;
private List mWifiP2pDeviceList = new ArrayList<>();
WifiP2pManager.PeerListListener mPeerListListener = new WifiP2pManager.PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceList wifiP2pDeviceList) {
mWifiP2pDeviceList.clear();
mWifiP2pDeviceList.addAll(wifiP2pDeviceList.getDeviceList());
}
};
/**
* 构造方法
*
* @param manager WifiP2pManager对象
* @param channel WifiP2pManager.Channel对象
* @param activity Wifip2pActivity 对象
*/
public WiFiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, Wifip2pActivity activity) {
super();
this.mManager = manager;
this.mChannel = channel;
this.mActivity = activity;
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION:
// Check to see if Wi-Fi is enabled and notify appropriate activity
Log.i(TAG, "onReceive: WIFI_P2P_STATE_CHANGED_ACTION");
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
// Wifi P2P is enabled
Log.i(TAG, "onReceive: Wifi P2P is enabled");
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.d(TAG, "onSuccess: ");
}
@Override
public void onFailure(int i) {
Log.d(TAG, "onFailure: ");
}
});
} else {
// Wi-Fi P2P is not enabled
Log.i(TAG, "onReceive: Wi-Fi P2P is not enabled");
}
break;
case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:
// Call WifiP2pManager.requestPeers() to get a list of current peers
Log.i(TAG, "onReceive: WIFI_P2P_PEERS_CHANGED_ACTION");
if (mManager == null) {
return;
}
mManager.requestPeers(mChannel, mPeerListListener);
break;
case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:
// Respond to new connection or disconnections
Log.i(TAG, "onReceive: WIFI_P2P_CONNECTION_CHANGED_ACTION");
// NetworkInfo
NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
// WifiP2pInfo
WifiP2pInfo wifiP2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
// WifiP2pGroup
WifiP2pGroup wifiP2pGroup = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
if (networkInfo.isConnected()) {
if (wifiP2pInfo.isGroupOwner) {
Toast.makeText(mActivity, "设备连接,本设备为GO", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(mActivity, "设备连接,本设备非GO", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(mActivity, "设备断开", Toast.LENGTH_LONG).show();
}
break;
case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:
// Respond to this device's wifi state changing
Log.i(TAG, "onReceive: WIFI_P2P_THIS_DEVICE_CHANGED_ACTION");
WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
Log.d(TAG, "onReceive: " +device.deviceAddress);
break;
case WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION:
Log.i(TAG, "onReceive: WIFI_P2P_DISCOVERY_CHANGED_ACTION");
break;
}
}
}
上述广播接收器用于监听系统关于WiFiP2P相关的广播。通常在onReceive()方法中,通过intent.getAction()方法获取到action,并根据action去匹配不同的关于WiFiP2P相关的广播,分别为:
- WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION
- WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION
- WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION
- WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
- WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION
WIFI_P2P_STATE_CHANGED_ACTION:WiFiP2P状态发生改变时的广播
WiFiP2P具体有两个状态:
- WifiP2pManager.WIFI_P2P_STATE_ENABLED:可用
- WifiP2pManager.WIFI_P2P_STATE_DISABLED:不可用
而该状态的获取是由:
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
当WiFiP2P状态为可用时,调用discoverPeers()方法开始搜索附近WiFiP2P设备:
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.d(TAG, "onSuccess: ");
}
@Override
public void onFailure(int i) {
Log.d(TAG, "onFailure: ");
}
});
WIFI_P2P_PEERS_CHANGED_ACTION:发现附近WiFiP2P设备时的广播
当搜索发现附近存在WiFiP2P设备时,调用requestPeers()方法开始获取附近WiFiP2P设备列表:
mManager.requestPeers(mChannel, mPeerListListener);
当成功获取附近WiFiP2P设备列表后,会回调侦听器 WifiP2pManager.PeerListListener 中的onPeersAvailable()方法,并传递一个 WifiP2pDeviceList 对象作为参数,可以用一个 List
WifiP2pManager.PeerListListener mPeerListListener = new WifiP2pManager.PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceList wifiP2pDeviceList) {
mWifiP2pDeviceList.clear();
mWifiP2pDeviceList.addAll(wifiP2pDeviceList.getDeviceList());
}
};
WIFI_P2P_CONNECTION_CHANGED_ACTION:连接状态发生改变时的广播
当连接状态发生改变时(如连接了一个设备,断开了一个设备),都会接收到该广播。当接收到该广播后,可以使用intent.getParcelableExtra()方法分别获取到 NetworkInfo , WifiP2pInfo , WifiP2pGroup 对象:
// 获取 NetworkInfo 对象
NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
// 获取 WifiP2pInfo 对象
WifiP2pInfo wifiP2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
// 获取 WifiP2pGroup 对象
WifiP2pGroup wifiP2pGroup = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
获取到上述对象后,可以使用networkInfo.isConnected()方法来判断连接状态具体是“设备连接”还是“设备断开”,还可以根据wifiP2pInfo.isGroupOwner的值来判断设备是否为GroupOwner:
if (networkInfo.isConnected()) {
if (wifiP2pInfo.isGroupOwner) {
Toast.makeText(mActivity, "设备连接,本设备为GroupOwner", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(mActivity, "设备连接,本设备非GroupOwner", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(mActivity, "设备断开", Toast.LENGTH_LONG).show();
}
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:当前设备状态发生改变时的广播
通常可以在这个广播中获取到当前设备的信息:
WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION:搜索状态发生改变时的广播
启动搜索:
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.d(TAG, "onSuccess: ");
}
@Override
public void onFailure(int i) {
Log.d(TAG, "onFailure: ");
}
});
停止搜索:
mManager.stopPeerDiscovery(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.d(TAG, "onSuccess: ");
}
@Override
public void onFailure(int i) {
Log.d(TAG, "onFailure: ");
}
});
连接设备
首先,选择一个需要连接的设备,并获取到该设备的 WifiP2pDevice 对象。然后,判断该设备的状态,设备状态通常为三种:
- WifiP2pDevice.AVAILABLE:可连接
- WifiP2pDevice.CONNECTED:已连接
- WifiP2pDevice.INVITED:已请求连接
根据不同的设备状态,进行不同的具体逻辑:
@Override
public void onClick(View view) {
// 获取到该设备的 WifiP2pDevice 对象
WifiP2pDevice wifiP2pDevice = mWifiP2pDeviceList.get(viewHolder.getAdapterPosition());
// 判断该设备的状态
switch (wifiP2pDevice.status) {
case WifiP2pDevice.AVAILABLE:
// 请求连接
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = wifiP2pDevice.deviceAddress;
mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.i(TAG, "connect success.");
}
@Override
public void onFailure(int i) {
Log.i(TAG, "connect failed.");
}
});
break;
case WifiP2pDevice.CONNECTED:
// 断开连接
mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.i(TAG, "removeGroup success.");
}
@Override
public void onFailure(int i) {
Log.i(TAG, "removeGroup failed.");
}
});
break;
case WifiP2pDevice.INVITED:
// 关闭连接请求
mManager.cancelConnect(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.i(TAG, "cancelConnect success.");
}
@Override
public void onFailure(int i) {
Log.i(TAG, "cancelConnect failed.");
}
});
break;
}
}
创建群组
GroupOwner创建Group:
private static final String NETWORKNAME_P60 = "DIRECT-HUAWEI-P60";
private static final String PASSPHRASE_P60 = "12345678";
private static void createWiFiP2PGroup(final Activity context, final WifiP2pManager manager, final WifiP2pManager.Channel channel) {
WifiP2pConfig config = new WifiP2pConfig.Builder()
.setGroupOperatingBand(WifiP2pConfig.GROUP_OWNER_BAND_AUTO)
.setNetworkName(NETWORKNAME_P60)
.setPassphrase(PASSPHRASE_P60)
.build();
manager.createGroup(channel, config, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.i(TAG, "onSuccess: createGroup");
Toast.makeText(context, "onSuccess: createGroup", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailure(int i) {
Log.i(TAG, "onFailure: createGroup " + i);
Toast.makeText(context, "onFailure: createGroup " + i, Toast.LENGTH_SHORT).show();
}
});
}
连接群组
Client连接GroupOwner:
public static void connectWiFiP2P(final Activity context, final WifiP2pManager manager, final WifiP2pManager.Channel channel, final WifiP2pDevice wifiP2pDevice) {
// 如果连接对象为群组GroupOwner
if (wifiP2pDevice.isGroupOwner()) {
Log.d(TAG, "connectWiFiP2P: " + wifiP2pDevice.deviceName);
WifiP2pConfig config;
if (needPassphrase) {
// 以PIN的方式连接
config = new WifiP2pConfig.Builder()
.setNetworkName(NETWORKNAME_iPhone)
.setPassphrase(PASSPHRASE_iPhone)
.build();
} else {
// 以默认的方式连接
config = new WifiP2pConfig();
config.deviceAddress = wifiP2pDevice.deviceAddress;
}
manager.connect(channel, config, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.i(TAG, "onSuccess: connect");
}
@Override
public void onFailure(int i) {
Log.i(TAG, "onFailure: connect " + i);
}
});
}
}
移除群组
GroupOwner移除Group:
manager.requestGroupInfo(channel, new WifiP2pManager.GroupInfoListener() {
@Override
public void onGroupInfoAvailable(WifiP2pGroup wifiP2pGroup) {
if (wifiP2pGroup != null) {
Log.i(TAG, "onGroupInfoAvailable: wifiP2pGroup != null");
manager.removeGroup(channel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
Log.i(TAG, "onSuccess: removeGroup");
}
@Override
public void onFailure(int i) {
Log.i(TAG, "onFailure: removeGroup");
}
});
} else {
Log.i(TAG, "onGroupInfoAvailable: wifiP2pGroup == null");
}
}
});