一:概述
直接看看效果
wifi直连是可以不用在WiFi环境下利用wifi传输数据的方式(当然在wifi环境下也可以)。
下面是wifi联盟的解释:
并且这个wifi直连,并不是只是像蓝牙两台设备互联,可以3台及以上(没有测试过上限是多少台)。但是有个局限,就是必须其中一台设备担任groupowner角色。其他的设备担任peer角色。
1.groupowner角色就像时一台服务器。其他是设备需要连接到此台设备上。
2.peer角色就像是一个客户端。 并且可以有多个
类似下面这样(画的略丑):
我们现在大体讲讲连接过程:
现在我们假设有三台设备 A,B,C。
A主动和C连接了,C就成了Groupowner角色(第一次被动连接的一般是groupowner角色,但是我的三星note3列外,他怎么都时peer角色。),那么A就是peer角色。
以下假设都是A已经和C的连接的基础上。
a.这时如果B主动去连C(Groupowner),那么直接连接成功。
b.这时如果B主动连接A,连接不成功。
c.这时如果A主动去连接B,这个时候B就是被邀请状态,会有一个弹窗提醒是否接受。
(三星note3接受后还是连接失败。华为手机接受邀请后连接成功),这个弹窗是系统弹窗。
连接成功后就需要开始传输数据了:
1.A和C连接后A可以直接获取到C的ip地址(192.168.49.1)。
2.C就使用ServerSocket监听一个端口(e.g 8988)。
3.A使用socket主动连接groupowner(Ip+端口)。
4.传输数据
5.关闭serverSocket
接下来有个问题:
C如何主动给A传输数据呢?C不知道A设备的Ip呀。这下我们就需要使用上面的步骤主动的将A自己的ip传输给groupowner。然后C就可以使用ip+端口利用socket传输数据了。
好了现在又来了个问题:
A和C(groupowner)连接,B也和C连接。那A如何传数据给B呀。
只能通过C传输,A->C->B。(做好了数据协议,还是比较容易。这次写的源码没有实现这步)
二:源码解读
申明权限:
//wifi p2p 是在Android 4.0以上使用
<uses-sdk android:minSdkVersion="14" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
需要使用以下对象
mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
mChannel = mManager.initialize(this, getMainLooper(), null);
然后再注册一个广播:
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
} 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)) {
}
解释下:
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 这个分支中可以获取自己这台设备的信息。
WIFI_P2P_PEERS_CHANGED_ACTION 是查找设备(调用discoverPeers()),
然后在这个分支中调用requestPeers()获取设备列表
WIFI_P2P_CONNECTION_CHANGED_ACTION wifi连接状态的改变。
WIFI_P2P_STATE_CHANGED_ACTION 就是wifi p2p可用还是不可用
我们先从查找设备开始:
查找成功后会发送一个广播。
mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
//会发送广播 WIFI_P2P_PEERS_CHANGED_ACTION
Log.e("xhc", "搜索成功");
}
@Override
public void onFailure(int reasonCode) {
Log.e("xhc", "搜索失败-->" + reasonCode);
}
});
看看在广播中做了什么:
调用了mManager.requestPeers,其第二个参数是WifiP2pManager.PeerListListener
回调的方法是:onPeersAvailable()。嘿嘿,我们就可以得到我们查找到的设备了。
else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
// Call WifiP2pManager.requestPeers() to get a list of current peers
if (mManager != null) {
//this 是指 WifiP2pManager.PeerListListener
mManager.requestPeers(mChannel, this);
}
}
...
@Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
stopLoading();
listDevice.clear();
listDevice.addAll(peers.getDeviceList());
adapter.setList(listDevice);
}
查找到了设备,我们就要开始连接啦。四不四很激动。
连接成功后也会发送一个广播。
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = wd.deviceAddress;
// 需要将address信息包装成WifiP2pConfig
mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
//广播:WIFI_P2P_CONNECTION_CHANGED_ACTION
}
@Override
public void onFailure(int reason) {
}
});
再看看我们在这个广播中做了什么:
else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
// Respond to new connection or disconnections
NetworkInfo networkInfo = intent
.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
requestConnectionInfo(GoodActivity.this);
// we are connected with the other device, request connection
// info to find group owner IP
}
}
...
//连接成功后,就去调用requestConnectionInfo(),请求信息。
public void requestConnectionInfo(
WifiP2pManager.ConnectionInfoListener listener) {
mManager.requestConnectionInfo(mChannel, listener);
}
...
//回调的方法
@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
//使用这个info就可以判断自己是不是groupowner角色
if (!info.isGroupOwner) {
//如果自己不是groupowner角色就将自己的ip传给groupowner端
//详情看代码
new ClientThread(...).start();
}
}
忘了说。在程序已启动就开始监听自己的端口(不管是groupowner,还是peer端)。
serverThread = new ServerThread(handler,this);
serverThread.start();
peer只需要传数据给groupowner端,groupowner需要传数据给不同的peer端,所以需要一个HashMap key(devicename),value(deviceip)来保存不同peer的ip。
当然如果想两个peer端互传数据,peer端也需要保存不同peer的ip或者devicename也可以。
好了。差不多打完收工。
加个好友共同学习(不是公众号):
因为小弟水平有限,如果有写的有问题,希望指出。
csdn写博客偶尔出现保存,发表失败。感觉内心有点小恐惧啊。