注:本文翻譯自Google官方的Android Developers Training文檔,譯者技術(shù)一般,由于喜愛(ài)安卓而產(chǎn)生了翻譯的念頭,純屬個(gè)人興趣愛(ài)好。
原文鏈接: http://developer.android.com/training/connect-devices-wirelessly/wifi-direct.html
Wi-Fi的P2P API允許設(shè)備連接到附近的設(shè)備,而不需要連接到網(wǎng)絡(luò)或熱點(diǎn)(Android的Wi-Fi P2P框架使用 Wi-Fi Direct? 認(rèn)證程序來(lái)編譯)Wi-Fi P2P允許你的應(yīng)用快速發(fā)現(xiàn)并連接到附近的設(shè)備,這一功能比起藍(lán)牙來(lái)說(shuō)更加強(qiáng)大。
這節(jié)課將向你展示如何使用Wi-Fi P2P來(lái)發(fā)現(xiàn)并連接附近的設(shè)備。
一). 設(shè)置應(yīng)用權(quán)限聲明
為了使用Wi-Fi P2P,需要添加 CHANGE_WIFI_STATE , ACCESS_WIFI_STATE 和 INTERNET 權(quán)限聲明到你的清單文件中。Wi-Fi P2P不需要一個(gè)網(wǎng)絡(luò)連接,但它使用了標(biāo)準(zhǔn)的Java套接字,而這需要 INTERNET 權(quán)限。所以你需要下列權(quán)限來(lái)使用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"
/>
...
二). 配置一個(gè)廣播接收器和一個(gè)P2P管理器
要使用Wi-Fi P2P,你需要監(jiān)聽(tīng)在某一事件發(fā)生時(shí),用來(lái)告知你的應(yīng)用的廣播Intents。在你的應(yīng)用中,實(shí)例化一個(gè) IntentFilter 并設(shè)置它為監(jiān)聽(tīng)下列事件:
指出Wi-Fi P2P已經(jīng)啟用
指出可以獲得的peer列表發(fā)生了變化
WIFI_P2P_CONNECTION_CHANGED_ACTION
指出Wi-Fi P2P連接的狀態(tài)發(fā)生了變化
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
指出設(shè)備的配置細(xì)節(jié)發(fā)生了改變
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() 方法的最后,獲取一個(gè) WifiP2pManager 的實(shí)例,然后調(diào)用其 initialize() 方法。這一方法返回一個(gè) WifiP2pManager.Channel 對(duì)象,在之后你將會(huì)用到它將你的應(yīng)用連接到Wi-Fi P2P框架。
@Override
Channel mChannel;
public
void
onCreate(Bundle savedInstanceState) {
....
mManager
=
(WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel
= mManager.initialize(
this
, getMainLooper(),
null
);
}
現(xiàn)在創(chuàng)建一個(gè)新的 BroadcastReceiver 類,來(lái)監(jiān)聽(tīng)系統(tǒng)的Wi-Fi P2P狀態(tài)的改變。在 onReceive() 方法中,添加一個(gè)條件分支來(lái)處理每一個(gè)之前列舉出來(lái)的P2P狀態(tài)變化。
@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處于活動(dòng)狀態(tài)時(shí),注冊(cè)intent過(guò)濾器和廣播接收器,并在activity被暫停時(shí)注銷它們。做這兩件事情最好的位置是在 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來(lái)搜索附近的設(shè)備,調(diào)用 discoverPeers() 方法。這一方法接收如下參數(shù):
- 當(dāng)你初始化P2P管理器時(shí)你所收回的 WifiP2pManager.Channel ;
- 一個(gè) WifiP2pManager.ActionListener 的實(shí)現(xiàn),具有一些在搜索成功或失敗時(shí)系統(tǒng)所要調(diào)用的方法。
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() 方法啟動(dòng)搜索進(jìn)程,然后迅速返回。系統(tǒng)會(huì)通知你搜索進(jìn)程是否被監(jiān)聽(tīng)器初始化成功。同時(shí)搜索會(huì)保持激活狀態(tài)知道一個(gè)連接被初始化或者一個(gè)P2P組被構(gòu)建完成。
四). 獲取Peers列表
現(xiàn)在寫(xiě)下獲取和處理Peers列表的代碼。首先實(shí)現(xiàn) WifiP2pManager.PeerListListener 接口,它提供了檢測(cè)到的Wi-Fi P2P的peer信息。請(qǐng)看下面的代碼:
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
;
}
}
}
現(xiàn)在修改你的廣播接收器的 onReceive() 方法,當(dāng)一個(gè)具有 WIFI_P2P_PEERS_CHANGED_ACTION 的intent被接收時(shí),來(lái)調(diào)用 requestPeers() 方法。你需要通過(guò)某種方法將監(jiān)聽(tīng)器傳遞給廣播接收器。一種方法是將它作為一個(gè)參數(shù)傳遞給廣播接收器的構(gòu)造函數(shù):
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"
);
}...
}
現(xiàn)在,一個(gè)具有 WIFI_P2P_PEERS_CHANGED_ACTION 的intent將會(huì)激活一個(gè)更新Peer列表的請(qǐng)求。
五). 與一個(gè)Peer發(fā)起連接
為了和一個(gè)Peer發(fā)起連接,創(chuàng)建一個(gè)新的 WifiP2pConfig 對(duì)象,然后從代表你想要連接的設(shè)備的 WifiP2pDevice 中把數(shù)據(jù)拷貝到這個(gè)對(duì)象里面。然后調(diào)用 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();
}
});
}
在這個(gè)代碼中實(shí)現(xiàn)的 WifiP2pManager.ActionListener 僅在當(dāng)初始化成功或失敗時(shí)向你發(fā)起通知。要監(jiān)聽(tīng)連接狀態(tài)的變化,需要實(shí)現(xiàn) WifiP2pManager.ConnectionInfoListener 接口。它的 onConnectionInfoAvailable() 回調(diào)函數(shù)將會(huì)在連接狀態(tài)變化后向你發(fā)出通知。在一些情況下,許多設(shè)備會(huì)向一個(gè)設(shè)備發(fā)起連接(比如一個(gè)多人連接的游戲,或者一個(gè)聊天的應(yīng)用),其中一個(gè)設(shè)備會(huì)被任命為一個(gè)“組所有者(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.
}
}
現(xiàn)在回到廣播接收器的 onReceive() 方法中,修改監(jiān)聽(tīng) WIFI_P2P_CONNECTION_CHANGED_ACTION 的intent的部分。當(dāng)這個(gè)intent接收到了以后,調(diào)用 requestConnectionInfo() 。這是一個(gè)異步的調(diào)用,所以結(jié)果會(huì)被之前你所提供的作為參數(shù)的連接信息監(jiān)聽(tīng)器接收:
...
}
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);
}
...
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

