WiFi對等API(P2P)不需要連接到網絡或熱點(Android的WiFi P2P框架符合WiFi編程指導規范),就允許應用程序連接到附近的設備。WiFiP2P允許應用程序快速的查找附近的設備,並與其交互。在連接范圍上超過了藍牙的能力。
本文介紹如何使用WiFi P2P來查找和連接附近的設備。
建立應用程序權限
為了使用WiFi P2P,要在應用程序的清單文件中添加CHANGE_WIFI_STATE、ACCESS_WIFI_STATE和INTERNET權限。WiFi P2P不要求互聯網連接,但是它使用標准的Java套接字,它要求INTERNET權限。因此使用WiFi P2P需要下例權限。
<manifestxmlns: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"/>
...
建立廣播接收器和對等管理器
要使用WiFi P2P,就要監聽廣播意圖,在確定事件發生時,告訴你的應用程序。在你的應用程序中,要實例化一個IntentFilter對象,並設置它來監聽以下動作:
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
指示設備的配置明細已經發生改變。
privatefinalIntentFilterintentFilter =newIntentFilter();
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Indicates achange in the Wi-Fi P2P status.
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
// Indicates a change inthe list of available peers.
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
// Indicates the state ofWi-Fi P2P connectivity has changed.
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
// Indicates this device'sdetails have changed.
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
...
}
在onCreate()方法中,獲取一個WifiP2pManager對象的實例,並調用它的initialize()方法。這個方法會返回一個WifiP2pManager.Channel對象,它會用於後續對WiFi P2P框架的連接。
@Override
Channel mChannel;
public void onCreate(Bundle savedInstanceState) {
....
mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(this, getMainLooper(), null);
}
現在創建一個新的BroadcastReceiver類,用於監聽系統的WiFi 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 somethingabout
// that.
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
// Connection state changed! We should probably do somethingabout
// 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 BroadcastReceiverwith 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);
}
啟動對等點發現功能
要使用Wi-Fi P2P來搜索附近的設備,就要調用discoverPeers()方法,這個方法需要以下參數:
WifiP2pManager.Channel:這個參數是在初始化對等管理器時獲取,用於把應用程序連接到Wi-Fi的P2P框架中;
WifiP2pManager.ActionListener接口實現:系統會在處理成功或失敗時調用該接口中對應的方法。
mManager.discoverPeers(mChannel,newWifiP2pManager.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.
}
});
記住,這只是啟動對等點發現功能。DiscoverPeers()方法啟動發現處理,然後立即返回。系統會通過調用你傳入的第二參數中的對應的接口方法,通知你對等點發現處理是否啟動成功。此外,發現處理會一直保持到連接被啟動或P2P組被組建。
獲取對等點的列表
現在要編寫獲取和處理對等點列表的代碼。首先實現WifiP2pManager.PeerListener接口,這個接口會提供有關已經發現的Wi-Fi的P2P對等點的信息。下列代碼演示這個處理:
privateList peers =newArrayList();
...
private PeerListListenerpeerListListener = new PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceListpeerList) {
// 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 ofavailable
// 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()方法,並把上面實現的監聽器傳遞給requestPeers()方法。你可以把這個監聽器作為廣播接收器的構造器的參數來傳遞。
publicvoid onReceive(Context context,Intent intent){
...
else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
// Requestavailable peers from the wifi p2p manager. This is an
//asynchronous call and the calling activity is notified with a
// callbackon PeerListListener.onPeersAvailable()
if (mManager != null) {
mManager.requestPeers(mChannel,peerListListener);
}
Log.d(WiFiDirectActivity.TAG, "P2P peerschanged");
}...
}
這樣,帶有WIFI-P2P_PEERS_CHANGED_ACTION的Intent操作就會觸發更新對等點列表的請求。
連接到對等點
為了連接到對等點,要創建一個新的WifiP2pConfig對象,並從代表你要連接的設備的WifiP2pDevice對象中復制數據到這個WifiP2pConfig對象中。然後調用connect()方法。
@Override
public void connect() {
// Pickingthe 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()回調方法在連接狀態變化時通知你。當有多個設備連接一個設備時,其中一個設備將會被指定為“組管理者”。
@Override
public voidonConnectionInfoAvailable(final WifiP2pInfo info) {
//InetAddress from WifiP2pInfo struct.
InetAddress groupOwnerAddress =info.groupOwnerAddress.getHostAddress());
// After thegroup 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);
}
...