赞
踩
什么是WiFi直连
通俗点说,它可以不通过网络,也不通过蓝牙,只要两台设备都支持WiFi直连,打开WiFi,不用连接任何WiFi,就可以进行信息的传输(请忽略下面两张图中的WiFi连接标志,因为其与WiFi的连接与否无关,打开就可以)。
在Android的设置->网络与互联网->WLAN->WLAN偏好设置->高级->WLAN直连
中可以找到关于Wi-Fi直连的设置,如下:
在参考其它博客时,写出来的代码并不能搜索到Wi-Fi中的其他设备,但是在这设置里面却可以。因此,找来其Android8.0的源代码作为参考,并成功解决问题。
源代码位置:
/packages/apps/Settings/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
我们可以通过系统的源代码来了解其相应的API的使用。下面是对应的系统源代码的分析:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
在onResume()
中注册广播接收,在onPause()
中取消广播接收。
/**
* Broadcast intent action to indicate whether Wi-Fi p2p is enabled or disabled. An
* extra {@link #EXTRA_WIFI_STATE} provides the state information as int.
*
* @see #EXTRA_WIFI_STATE
*/
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
/**
* Broadcast intent action indicating that the available peer list has changed. This
* can be sent as a result of peers being found, lost or updated.
*
* <p> An extra {@link #EXTRA_P2P_DEVICE_LIST} provides the full list of
* current peers. The full list of peers can also be obtained any time with
* {@link #requestPeers}.
*
* @see #EXTRA_P2P_DEVICE_LIST
*/
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
/**
* Broadcast intent action indicating that the state of Wi-Fi p2p connectivity
* has changed. One extra {@link #EXTRA_WIFI_P2P_INFO} provides the p2p connection info in
* the form of a {@link WifiP2pInfo} object. Another extra {@link #EXTRA_NETWORK_INFO} provides
* the network info in the form of a {@link android.net.NetworkInfo}. A third extra provides
* the details of the group.
*
* @see #EXTRA_WIFI_P2P_INFO
* @see #EXTRA_NETWORK_INFO
* @see #EXTRA_WIFI_P2P_GROUP
*/
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
/**
* Broadcast intent action indicating that this device details have changed.
*/
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
/**
* Broadcast intent action indicating that peer discovery has either started or stopped.
* One extra {@link #EXTRA_DISCOVERY_STATE} indicates whether discovery has started
* or stopped.
*
* <p>Note that discovery will be stopped during a connection setup. If the application tries
* to re-initiate discovery during this time, it can fail.
*/
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
/**
* Broadcast intent action indicating that remembered persistent groups have changed.
* @hide
*/
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
对上面广播的处理(需要留意的是,这些数据都是从Intent
中取出来的):
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
mWifiP2pEnabled = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
WifiP2pManager.WIFI_P2P_STATE_DISABLED) == WifiP2pManager.WIFI_P2P_STATE_ENABLED;
handleP2pStateChanged();
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
// 这里是直接从Intent中取出了列表数据
mPeers = (WifiP2pDeviceList) intent.getParcelableExtra(
WifiP2pManager.EXTRA_P2P_DEVICE_LIST);
handlePeersChanged();
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
if (mWifiP2pManager == null) return;
NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
WifiP2pManager.EXTRA_NETWORK_INFO);
// 此处的WifiP2pInfo可以让我们获取到GO(Group Owner)的IP
// 这是最神奇的地方,没有网络的连接,却得到了IP
// 然后我们可以通过这个IP,与GO进行socket通信
WifiP2pInfo wifip2pinfo = (WifiP2pInfo) intent.getParcelableExtra(
WifiP2pManager.EXTRA_WIFI_P2P_INFO);
if (networkInfo.isConnected()) {
if (DBG) Log.d(TAG, "Connected");
} else if (mLastGroupFormed != true) {
//start a search when we are disconnected
//but not on group removed broadcast event
startSearch();
}
mLastGroupFormed = wifip2pinfo.groupFormed;
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
mThisDevice = (WifiP2pDevice) intent.getParcelableExtra(
WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
if (DBG) Log.d(TAG, "Update device info: " + mThisDevice);
updateDevicePref();
} else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {
int discoveryState = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE,
WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
if (DBG) Log.d(TAG, "Discovery state changed: " + discoveryState);
if (discoveryState == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED) {
updateSearchMenu(true);
} else {
updateSearchMenu(false);
}
} else if (WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION.equals(action)) {
if (mWifiP2pManager != null) {
mWifiP2pManager.requestPersistentGroupInfo(mChannel, WifiP2pSettings.this);
}
}
}
};
点击菜单栏上的搜索后,会进行如下操作。然后会接收到相应的广播,刷新是在对相应广播的处理中进行的。
private void startSearch() {
if (mWifiP2pManager != null && !mWifiP2pSearching) {
mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
public void onSuccess() {
}
public void onFailure(int reason) {
if (DBG) Log.d(TAG, " discover fail " + reason);
}
});
}
}
在对广播的处理中,设备变化的处理主要是靠handlePeersChanged()
:
private void handlePeersChanged() {
mPeersGroup.removeAll();
mConnectedDevices = 0;
if (DBG) Log.d(TAG, "List of available peers");
for (WifiP2pDevice peer: mPeers.getDeviceList()) {
if (DBG) Log.d(TAG, "-> " + peer);
mPeersGroup.addPreference(new WifiP2pPeer(getActivity(), peer));
if (peer.status == WifiP2pDevice.CONNECTED) mConnectedDevices++;
}
if (DBG) Log.d(TAG, " mConnectedDevices " + mConnectedDevices);
}
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = mSelectedWifiPeer.device.deviceAddress;
int forceWps = SystemProperties.getInt("wifidirect.wps", -1);
if (forceWps != -1) {
config.wps.setup = forceWps;
} else {
if (mSelectedWifiPeer.device.wpsPbcSupported()) {
config.wps.setup = WpsInfo.PBC;
} else if (mSelectedWifiPeer.device.wpsKeypadSupported()) {
config.wps.setup = WpsInfo.KEYPAD;
} else {
config.wps.setup = WpsInfo.DISPLAY;
}
}
mWifiP2pManager.connect(mChannel, config,
new WifiP2pManager.ActionListener() {
public void onSuccess() {
if (DBG) Log.d(TAG, " connect success");
}
public void onFailure(int reason) {
Log.e(TAG, " connect fail " + reason);
Toast.makeText(getActivity(),
R.string.wifi_p2p_failed_connect_message,
Toast.LENGTH_SHORT).show();
}
});
对这段代码中,有一个使用了android.os.SystemProperties
这个{@hide}
修饰的类。我们可以考虑通过反射的方式来近行调用,如下:
private int getSystemProp(){
try {
Class<?> cls = Class.forName("android.os.SystemProperties");
Method m = cls.getDeclaredMethod("get", String.class, String.class);
return Integer.parseInt((String)m.invoke(null,"wifidirect.wps","-1"));
} catch (Exception e) {
Log.i(TAG, "E = " + e.getMessage());
e.printStackTrace();
}
return -1;
}
//disconnect dialog listener
mDisconnectListener = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
if (mWifiP2pManager != null) {
mWifiP2pManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
public void onSuccess() {
if (DBG) Log.d(TAG, " remove group success");
}
public void onFailure(int reason) {
if (DBG) Log.d(TAG, " remove group fail " + reason);
}
});
}
}
}
};
//cancel connect dialog listener
mCancelConnectListener = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
if (mWifiP2pManager != null) {
mWifiP2pManager.cancelConnect(mChannel,
new WifiP2pManager.ActionListener() {
public void onSuccess() {
if (DBG) Log.d(TAG, " cancel connect success");
}
public void onFailure(int reason) {
if (DBG) Log.d(TAG, " cancel connect fail " + reason);
}
});
}
}
}
};
mRenameListener = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
if (mWifiP2pManager != null) {
String name = mDeviceNameText.getText().toString();
if (name != null) {
for (int i = 0; i < name.length(); i++) {
char cur = name.charAt(i);
if(!Character.isDigit(cur) && !Character.isLetter(cur)
&& cur != '-' && cur != '_' && cur != ' ') {
Toast.makeText(getActivity(),
R.string.wifi_p2p_failed_rename_message,
Toast.LENGTH_LONG).show();
return;
}
}
}
mWifiP2pManager.setDeviceName(mChannel,
mDeviceNameText.getText().toString(),
new WifiP2pManager.ActionListener() {
public void onSuccess() {
if (DBG) Log.d(TAG, " device rename success");
}
public void onFailure(int reason) {
Toast.makeText(getActivity(),
R.string.wifi_p2p_failed_rename_message,
Toast.LENGTH_LONG).show();
}
});
}
}
}
};
对于GO来说,当与GC(Group Client)连接完成后,也就是接收到WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION
广播后,需要充当的角色是服务器,所以可以利用Java中socket通信中的SocketServer
来阻塞当前的线程(子),监听所受到的socket。如下:
if (wifip2pinfoG.isGroupOwner) { // 充当服务器
new Thread(new Runnable() {// 不能阻塞主线程
@Override // 同时也不允许在主线程中进行网络通信
public void run() { // 所以开启子线程
try {
ServerSocket server = new ServerSocket(6666, 100, wifip2pinfoG.groupOwnerAddress);
Socket socket;
while((socket = server.accept()) != null){
InputStream bis = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(bis));
String info = br.readLine();
final String log = info;
// 显示出所接收的信息
Log.e("TAG", log);
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(context, log, Toast.LENGTH_SHORT).show();
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
Toast.makeText(context, "服务器已启动", Toast.LENGTH_SHORT).show();
} else { // 充当客户端
Toast.makeText(context, "可发送消息", Toast.LENGTH_SHORT).show();
}
对GC来说,当与GO连接好了之后,即可发送给GO发送消息。如下:
new Thread(new Runnable() {// 不能在主线程中进行网络通信,需要子线程
@Override
public void run() {
try {
sendSocket = new Socket(wifip2pinfoG.groupOwnerAddress, 6666);
OutputStreamWriter osw = new OutputStreamWriter(sendSocket.getOutputStream());
// getInfo()是一个输入框,没有输入时默认返回hello
osw.write(getInfo());
osw.flush();
Log.e("TAG", "info sended");
// 注意这里,需要及时关闭socket
sendSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-526ced5128.css" rel="stylesheet">
</div>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。