wifi p2p api可以让设备和身边的设备相连接,而不需要网络或者热点。android的wifi p2p框架使用了wifi-direct认证的程序。wifi p2p让你的应用快速找到并连接周围的设备,与之教育,这个范围要比蓝牙更广。
这节课教你怎样使用wifi直连来找到和连接周围的设备。
建立应用的权限
要使用wifip2p,需要在manifest中添加权限CHANGE_WIFI_SATE,ACCESS_WIFI_STATE和INTERNET权限。wifip2p不需要互联网连接,但是它使用标准的javasocket,所以需要INTERNET权限。所以你需要遵循下面的权限类使用wifip2p。
<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管理器
要使用wifi p2p你需要监听广播intent来监听特定的事件。在你的app中设立一个intentFileter来监听下面的事情。
WIFI_P2P_STATE_CHANGED_ACTION
WIFI_P2P_CONNECTION_CHANGED_ACTION
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对象,一会你讲在wifip2p框架当中使用。
@Override Channel mChannel; public void onCreate(Bundle savedInstanceState) { .... mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); mChannel = mManager.initialize(this, getMainLooper(), null); }下面创建一个BroadCastReceiver来监听系统的wifip2p的状态变化。在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.判断wifi P2p的模式有无变化,如果有的话就想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 filter和 broadcastReceiver,当activity pause的时候注销他们。最好的是在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); }
初始化配对发现
想要发现附近的 wifiP2P设备,需要使用下面的参数调用discoverPeers方法。
- 当你初始化peer to peer Manager的时候返回的WifiP2pManager对象。
- 构建一个WifiP2pManager.ActionListener,系统将会在发现设备或者失败的时候调用其中的方法。
mManager.discoverPeers(mChannel,new WifiP2pManager.ActionListener){
@Override
public void onSuccess(){
但你发现初始化成功的时候就会调用这里。
还没有服务被实际的发现呢,所以这个方法经常被留空。配对发现在onRecive方法中,细节见下面。
}
@Overide
public void onFailure(int reasonCode){
当发现初始化失败的时候调用这里,警告用户出现了一些错误。
}
}
记住,这仅仅初始化了配对的发下。discoverPeers()方法启动了发现程序,并立即返回了。系统在发现初始化成功的时候会调用你给的 action listener中的方法来通知你成功。同样,发现会一直持续,一直到初始化成功或者p2p小组建立。
获取配对伙伴列表
现在写获取对象 和处理列表的代码。先实现WifiP2pManager.PeerListListener的接口,它可以提供发现的wifiP2p设备的 列表。下面的代码片作为例子:
private List peers=new ArrayList();
private PeerListListener peerListListener=new PeerListListener(){
@Override
public void onPeeersAvailable(WifiP2pDeviceList peerList){
//外面是旧的,里面是新的
peers.clear();
peers.addAll(peerList.getDeviceList());
//如果有AdapterView被这个数据填充了,那么这时要通知更新。例如,如果你一个可用配对的 列表。
((WiFiPeerListAdapter)getListAdapter()).notifyDataSetChanged();
if(peers.size()==0){
Log.d(WiFiDirectActivity.TAG,"No device found");
return ;
}
}
}
现在修改你的broadcast receiver在接受到action 为WIFI_p2p_PEERSpCHANGED_ACTIONS的时候在onReceiver中调用requestPeers方法。
如论如何你都要吧listener传入到receiver当中。一种方法是作为broadcastReceiver的参数传递进去。
public void onReceive(Context context, Intent intent){
……
else if(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)){
//从wifip2p管理者里面请求可用的对象。指示第一个异步调用,并且调换那个的activity会调用PeerListLisener.onPeersAvailiable()来被通知。
}
Log.d(WifiDirectActivity.TAG,"P2P peers changed);
}
现在,使用action WIFI_P2P_PEERS_CHANGED_ACTION的intent将会触发一个更新配对列表的请求。
连接到一个配对
为了连接一个配对,创建一个新的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接口只能通知初始化成功和失败。想要监听其他的连接状态变化,实现WifiP2pManger.ConnnectionInfoListener接口。其中的onConnetionInfoAvailable方法将会在网络状态发生变化的时候回调。 当有多个设备要连接到一个设备的时候(比如说多个玩家的游戏,或者一个聊天app),那么这个设备应该作为群主。(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. } }
回到BroadCast Receiver的onReceive方法中,定制监听一个WIFI_P2P_CONNECTION_CHANGED_ACTION intent 模块。当收到这个intent的时候,调用 requestConnectionInfo()方法。这是一个异步的调用,结果将会通过作为参数传入的 listener对象中的方法来接收。