WIFI-P2P允许你的应用快速查找和交互附近的设备,这是一个在蓝牙范围外的功能。
WIFI-P2P API允许应用在无需连接网络或热点的情况下连接附近设备。如果你的应用被设计成一个安全的近距离的网络,WIFI是一个更合适的选择。
设置应用的权限
<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,告诉你的应用某些事情已经发生。实例化IntentFilter并且设置如下监听:
WIFI_P2P_STATE_CHANGED_ACTION:表示WIFI-P2P是否启动。
WIFI_P2P_PEERS_CHANGED_ACTION:表示可用的端口列表已经改变。
WIFI_P2P_CONNECTION_CHANGED_ACTION:表示Wi-Fi P2P连接状态已发生变化。
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:表示此设备的配置详细信息已更改。
private final IntentFilter intentFilter = new IntentFilter();
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
}
获取WifiP2pManager并且调用initialize()方法,返回一个WifiP2pManager.Channel类。
...
private WifiP2pManager.Channel mChannel;
private WifiP2pManager mManager;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
mManager = (WifiP2pManager) this.getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(this,getMainLooper(),null);
}
接下来创建一个BroadcastReceiver类,它用于监听系统的Wi-Fi P2P的状态变化,在onReceive()方法中,需要添加一些基本的判断条件来处理每种P2P的状态并处理:
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
private MainActivity activity;
private WifiP2pManager mManager;
private Channel mChannel;
public WiFiDirectBroadcastReceiver(WifiP2pManager mManager, Channel mChannel, MainActivity activity) {
this.activity = activity;
this.mManager = mManager;
this.mChannel = mChannel;
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)){
//确认WIFI-P2P是否启动
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)){
//对等列表已更改!我们应该做点什么
} else if(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)){
//连接状态改变
} else if(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)){
//此设备的配置详细信息已更改
Toast.makeText(context.getApplicationContext(),action,Toast.LENGTH_SHORT).show();
}
}
}
最后,将广播接收器与意图过滤器添加到上下文中,并需要在Activity暂停的时候注销这个广播接收器。放置这些代码的最佳位置就是onResume()方法与onPause()方法。
@Override
protected void onResume() {
super.onResume();
receiver = new WiFiDirectBroadcastReceiver(mManager,mChannel,this);
registerReceiver(receiver,intentFilter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
public void setIsWifiP2pEnabled(boolean isWifiP2pEnabled){
if(isWifiP2pEnabled){
Toast.makeText(this,"WifiP2p连接成功",Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this,"WifiP2p连接失败",Toast.LENGTH_SHORT).show();
}
}
初始化端口搜索
调用WifiP2pManager.discoverPeers()他继承WifiP2pManager.ActionListener接口用于监听搜索成功与否
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
}
@Override
public void onFailure(int reason) {
//提醒用户出错了
}
});
要记住,这里只是初始化了端点搜索。discoverPeers()方法启动搜索进程后会立即返回。如果端点搜索进程成功初始化,那么系统会自动调用初始化时设置的回调方法。另外,端点搜索功能会一直保持在活动状态,直到连接初始化完成或者P2P组建立连接。
获取对等设备列表
现在获取对等列表,首先需要继承* WifiP2pManager.PeerListListener*接口,它提供了WI-FI P2P所搜索到的端点信息。添加代码到BroadcastReceiver类。
private List<WifiP2pDevice> peers = new ArrayList<>();
private WifiP2pManager.PeerListListener peerListListener = new WifiP2pManager.PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceList peerList) {
List<WifiP2pDevice> refreshedPeers = (List<WifiP2pDevice>) peerList.getDeviceList();
if(!refreshedPeers.equals(peers)){
peers.clear();
peers.addAll(refreshedPeers);
//可以通过一个ListView显示
//需要多个人连接才能获取他人信息
}
if(peers.size() == 0){
Log.d(WiFiDirectActivity.TAG, "No devices found");
return;
}
}
};
现在需要重新设置WIFI_P2P_PEERS_CHANGED_ACTION的验证。
public void onReceive(Context context, Intent intent) {
...
else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
if (mManager != null) {
mManager.requestPeers(mChannel, peerListListener);
}
Log.d(WiFiDirectActivity.TAG, "P2P peers changed");
}...
}
连接一个对等设备
为了连接一个端口,需要创建一个新的WifiP2pConfig对象,并且从WifiP2pDevice拷贝数据。代表你想连接这个设备。WifiP2pManager调用connect()方法。
WifiP2pDevice device = peers.get(position);
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
mManager.connect(mChannel, config, new ActionListener() {
@Override
public void onSuccess() {
}
@Override
public void onFailure(int reason) {
Toast.makeText(WiFiDirectActivity.this, "连接失败",
Toast.LENGTH_SHORT).show();
}
});
如果在你的组里面每一个设备都支持wifi直连,当你连接时,你不需要明确的告诉这个组密码。为了允许不支持WIFI直连的设备加入群,你需要调用requestGroupInfo()来检索密码。
mManager.requestGroupInfo(mChannel, new WifiP2pManager.GroupInfoListener() {
@Override
public void onGroupInfoAvailable(WifiP2pGroup group) {
if(group!=null){
String groupPassword = group.getPassphrase();
}
}
});
上述代码中的WifiP2pManager.ActionListener接口只有在初始化成功或者失败的情况下才会调用。如果要监听连接状态的变化,需要实现WifiP2pManager.ConnectionInfoListener接口,它的方法onConnectionInfoAvailable()会在连接状态发生变化的时候回调。在多台设备连接一台设备的情况下(比如多人互动的游戏或者聊天类的APP),其中一台设备会被指定为”群组的拥有者”。
else if(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)){
Log.d("connection",action);
//连接状态改变
if(mManager==null){
Log.d("connection","null");
return;
}
NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if(networkInfo.isConnected()){
mManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
//获取拥有者的地址。
String groupOwnerAddress = info.groupOwnerAddress.getHostAddress();
Log.d("connection",groupOwnerAddress);
if(info.groupFormed&&info.isGroupOwner){
//做任何只属于管理者的的任务。一个常见的情况是创建一个组所有者线程和接受权限
}else if(info.groupFormed){
//其他设备是对等,在这种情况下需要管理者创建对等线程。
}
}
});
}
}
创建一个组
如果你想让设备作为一个网络运用你的应用包括传统设备,设备不支持WIFI Direct–你应该以相同的顺序步骤在连接对等部分。除非你创建一个新的WifiP2pManager.ActionListener()使用createGroup()替代connect()。
mManager.createGroup(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
//设备准备接受来自同等级的传入连接。
}
@Override
public void onFailure(int reason) {
Toast.makeText(WiFiDirectActivity.this, "P2P 组创建失败",
Toast.LENGTH_SHORT).show();
}
});
注:如果在一个网络支持Wi-Fi Direct的所有设备,你可以使用connect()方法在每个设备因为方法然后创建组和拥有者自动选择一组。
你创建一个组后,你可以调用requestgroupinfo()检索网络上的节点的详细信息,包括设备名称和连接状态。