使用 WiFi 建立 P2P 连接
Wi-Fi 点对点(P2P)API 允许应用程序在无需连接到网络和热点的情况下连接到附近的设备。(Android Wi-Fi P2P 使用 Wi-Fi Direct™ 验证程序进行编译)。Wi-Fi P2P 技术使得应用程序可以快速发现附近的设备并与之交互。相比于蓝牙技术,Wi-Fi P2P 的优势是具有较大的连接范围。
本节主要内容是使用 Wi-Fi P2P 技术发现并连接到附近的设备。
使用 Wi-Fi P2P 技术,需要添加 ,ACCESS_WIFI_STATE 以及 三种权限到应用的 manifest 文件。Wi-Fi P2P 技术虽然不需要访问互联网,但是它会使用标准的 Java socket(需要 INTERNET 权限)。下面是使用 Wi-Fi P2P 技术需要申请的权限。
使用 Wi-Fi P2P 的时候,需要侦听当某个事件出现时发出的broadcast intent。在应用中,实例化一个 ,并将其设置为侦听下列事件:
指示 Wi-Fi P2P 是否开启
WIFI_P2P_CONNECTION_CHANGED_ACTION
表明Wi-Fi P2P的连接状态发生了改变
指示设备的详细配置发生了变化
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Indicates a change in the Wi-Fi P2P status.
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
// Indicates a change in the list of available peers.
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
// Indicates the state of Wi-Fi P2P connectivity has changed.
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
// Indicates this device's details have changed.
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
...
}
在 onCreate() 方法的最后,需要获得 的实例,并调用它的 initialize() 方法。该方法将返回 对象。
我们的应用将在后面使用该对象连接 Wi-Fi P2P 框架。
@Override
Channel mChannel;
public void onCreate(Bundle savedInstanceState) {
....
mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(this, getMainLooper(), null);
}
接下来,创建一个新的 BroadcastReceiver 类侦听系统中 Wi-Fi P2P 状态的变化。在 方法中,加入对上述四种不同 P2P 状态变化的处理。
最后,在主 activity 开启时,加入注册 intent filter 和 broadcast receiver 的代码,并在 activity 暂停或关闭时,注销它们。上述做法最好放在 onResume() 和 onPause() 方法中。
/** register the BroadcastReceiver with the intent values to be matched */
@Override
public void onResume() {
super.onResume();
registerReceiver(receiver, intentFilter);
}
public void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
- 上节中调用 WifiP2pManager 的 initialize() 函数获得的 WifiP2pManager.Channel 对象
- 一个对 接口的实现,包括了当系统成功和失败发现所调用的方法。
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
// Code for when the discovery initiation is successful goes here.
// No services have actually been discovered yet, so this method
// can often be left blank. Code for peer discovery goes in the
// onReceive method, detailed below.
}
@Override
public void onFailure(int reasonCode) {
// Code for when the discovery initiation fails goes here.
// Alert the user that something went wrong.
}
});
需要注意的是,这仅仅表示对Peer发现(Peer Discovery)完成初始化。discoverPeers() 方法开启了发现过程并且立即返回。系统会通过调用 WifiP2pManager.ActionListener 中的方法通知应用对等节点发现过程初始化是否正确。同时,对等节点发现过程本身仍然继续运行,直到一条连接或者一个 P2P 小组建立。
在完成对等节点发现过程的初始化后,我们需要进一步获取附近的对等节点列表。第一步是实现 接口。该接口提供了 Wi-Fi P2P 框架发现的对等节点信息。下列代码实现了相应功能:
接下来,完善 Broadcast Receiver 的 onReceiver() 方法。
当收到 WIFI_P2P_PEERS_CHANGED_ACTION 事件时,
调用 方法获取对等节点列表。我们需要将 WifiP2pManager.PeerListListener 传递给 receiver。一种方法是在 broadcast receiver 的构造函数中,将对象作为参数传入。
public void onReceive(Context context, Intent intent) {
...
else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
// Request available peers from the wifi p2p manager. This is an
// asynchronous call and the calling activity is notified with a
// callback on PeerListListener.onPeersAvailable()
if (mManager != null) {
mManager.requestPeers(mChannel, peerListListener);
}
Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
}...
}
现在,一个带有 WIFI_P2P_PEERS_CHANGED_ACTION action 的 intent 将触发应用对 Peer 列表的更新。
为了连接到一个对等节点,我们需要创建一个新的 对象,并将要连接的设备信息从表示我们想要连接设备的 WifiP2pDevice 拷贝到其中。然后调用 方法。
public void connect() {
// Picking the first device found on the network.
WifiP2pDevice device = peers.get(0);
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
mManager.connect(mChannel, config, new ActionListener() {
@Override
public void onSuccess() {
// WiFiDirectBroadcastReceiver will notify us. Ignore for now.
}
@Override
public void onFailure(int reason) {
Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
Toast.LENGTH_SHORT).show();
}
});
}
在本段代码中的 WifiP2pManager.ActionListener 实现仅能通知我们初始化的成功或失败。想要监听连接状态的变化,需要实现 接口。接口中的 onConnectionInfoAvailable() 回调函数会在连接状态发生改变时通知应用程序。当有多个设备同时试图连接到一台设备时(例如多人游戏或者聊天群),这一台设备将被指定为“群主”(group owner)。
此时,回头继续完善 broadcast receiver 的 onReceive()
方法,并修改对 intent 的监听部分的代码。当接收到该 intent 时,调用 requestConnectionInfo() 方法。此方法为异步,所以结果将会被我们提供的 所获取。
...
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
if (mManager == null) {
return;
}
NetworkInfo networkInfo = (NetworkInfo) intent
.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
// We are connected with the other device, request connection
// info to find group owner IP
mManager.requestConnectionInfo(mChannel, connectionListener);
}