注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好。
原文链接: http://developer.android.com/training/connect-devices-wirelessly/wifi-direct.html
Wi-Fi的P2P API允许设备连接到附近的设备,而不需要连接到网络或热点(Android的Wi-Fi P2P框架使用 Wi-Fi Direct™ 认证程序来编译)Wi-Fi P2P允许你的应用快速发现并连接到附近的设备,这一功能比起蓝牙来说更加强大。
这节课将向你展示如何使用Wi-Fi P2P来发现并连接附近的设备。
一). 设置应用权限声明
为了使用Wi-Fi P2P,需要添加 CHANGE_WIFI_STATE , ACCESS_WIFI_STATE 和 INTERNET 权限声明到你的清单文件中。Wi-Fi P2P不需要一个网络连接,但它使用了标准的Java套接字,而这需要 INTERNET 权限。所以你需要下列权限来使用Wi-Fi P2P。
<
manifest
xmlns:android
="http://schemas.android.com/apk/res/android"
package
="com.example.android.nsdchat"
...
<uses-permission
android:required
="true"
android:name
="android.permission.ACCESS_WIFI_STATE"
/>
<
uses-permission
android:required
="true"
android:name
="android.permission.CHANGE_WIFI_STATE"
/>
<
uses-permission
android:required
="true"
android:name
="android.permission.INTERNET"
/>
...
二). 配置一个广播接收器和一个P2P管理器
要使用Wi-Fi P2P,你需要监听在某一事件发生时,用来告知你的应用的广播Intents。在你的应用中,实例化一个 IntentFilter 并设置它为监听下列事件:
指出Wi-Fi P2P已经启用
指出可以获得的peer列表发生了变化
WIFI_P2P_CONNECTION_CHANGED_ACTION
指出Wi-Fi P2P连接的状态发生了变化
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
指出设备的配置细节发生了改变
private
final
IntentFilter intentFilter =
new
IntentFilter();
...
@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() 方法的最后,获取一个 WifiP2pManager 的实例,然后调用其 initialize() 方法。这一方法返回一个 WifiP2pManager.Channel 对象,在之后你将会用到它将你的应用连接到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状态的改变。在 onReceive() 方法中,添加一个条件分支来处理每一个之前列举出来的P2P状态变化。
@Override
public
void
onReceive(Context context, Intent intent) {
String action
=
intent.getAction();
if
(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
//
Determine if Wifi P2P mode is enabled or not, alert
//
the Activity.
int
state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1
);
if
(state ==
WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
activity.setIsWifiP2pEnabled(
true
);
}
else
{
activity.setIsWifiP2pEnabled(
false
);
}
}
else
if
(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
//
The peer list has changed! We should probably do something about
//
that.
}
else
if
(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
//
Connection state changed! We should probably do something about
//
that.
}
else
if
(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
DeviceListFragment fragment
=
(DeviceListFragment) activity.getFragmentManager()
.findFragmentById(R.id.frag_list);
fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra(
WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
}
}
最后,添加一些代码,在主activity处于活动状态时,注册intent过滤器和广播接收器,并在activity被暂停时注销它们。做这两件事情最好的位置是在 onResume() 和 onPause() 方法中。
/**
register the BroadcastReceiver with the intent values to be matched
*/
@Override
public
void
onResume() {
super
.onResume();
receiver
=
new
WiFiDirectBroadcastReceiver(mManager, mChannel,
this
);
registerReceiver(receiver, intentFilter);
}
@Override
public
void
onPause() {
super
.onPause();
unregisterReceiver(receiver);
}
三). 初始化Peer搜索
要使用Wi-Fi P2P来搜索附近的设备,调用 discoverPeers() 方法。这一方法接收如下参数:
- 当你初始化P2P管理器时你所收回的 WifiP2pManager.Channel ;
- 一个 WifiP2pManager.ActionListener 的实现,具有一些在搜索成功或失败时系统所要调用的方法。
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搜索。 discoverPeers() 方法启动搜索进程,然后迅速返回。系统会通知你搜索进程是否被监听器初始化成功。同时搜索会保持激活状态知道一个连接被初始化或者一个P2P组被构建完成。
四). 获取Peers列表
现在写下获取和处理Peers列表的代码。首先实现 WifiP2pManager.PeerListListener 接口,它提供了检测到的Wi-Fi P2P的peer信息。请看下面的代码:
private
List peers =
new
ArrayList();
...
private
PeerListListener peerListListener =
new
PeerListListener() {
@Override
public
void
onPeersAvailable(WifiP2pDeviceList peerList) {
//
Out with the old, in with the new.
peers.clear();
peers.addAll(peerList.getDeviceList());
//
If an AdapterView is backed by this data, notify it
//
of the change. For instance, if you have a ListView of available
//
peers, trigger an update.
((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged();
if
(peers.size() == 0
) {
Log.d(WiFiDirectActivity.TAG,
"No devices found"
);
return
;
}
}
}
现在修改你的广播接收器的 onReceive() 方法,当一个具有 WIFI_P2P_PEERS_CHANGED_ACTION 的intent被接收时,来调用 requestPeers() 方法。你需要通过某种方法将监听器传递给广播接收器。一种方法是将它作为一个参数传递给广播接收器的构造函数:
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 的intent将会激活一个更新Peer列表的请求。
五). 与一个Peer发起连接
为了和一个Peer发起连接,创建一个新的 WifiP2pConfig 对象,然后从代表你想要连接的设备的 WifiP2pDevice 中把数据拷贝到这个对象里面。然后调用 connect() 方法。
@Override
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 仅在当初始化成功或失败时向你发起通知。要监听连接状态的变化,需要实现 WifiP2pManager.ConnectionInfoListener 接口。它的 onConnectionInfoAvailable() 回调函数将会在连接状态变化后向你发出通知。在一些情况下,许多设备会向一个设备发起连接(比如一个多人连接的游戏,或者一个聊天的应用),其中一个设备会被任命为一个“组所有者(group owner)”。
@Override
public
void
onConnectionInfoAvailable(
final
WifiP2pInfo info) {
//
InetAddress from WifiP2pInfo struct.
InetAddress groupOwnerAddress =
info.groupOwnerAddress.getHostAddress());
//
After the group negotiation, we can determine the group owner.
if
(info.groupFormed &&
info.isGroupOwner) {
//
Do whatever tasks are specific to the group owner.
//
One common case is creating a server thread and accepting
//
incoming connections.
}
else
if
(info.groupFormed) {
//
The other device acts as the client. In this case,
//
you'll want to create a client thread that connects to the group
//
owner.
}
}
现在回到广播接收器的 onReceive() 方法中,修改监听 WIFI_P2P_CONNECTION_CHANGED_ACTION 的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);
}
...

