什么是WiFi直连 通俗点说,它可以不通过网络,也不通过蓝牙,只要两台设备都支持WiFi直连,打开WiFi ,不用连接任何WiFi ,就可以进行信息的传输(请忽略下面两张图中的WiFi连接标志,因为其与WiFi的连接与否无关,打开就可以)。
在Android的设置->网络与互联网->WLAN->WLAN偏好设置->高级->WLAN直连
中可以找到关于Wi-Fi直连的设置,如下:
在参考其它博客时,写出来的代码并不能搜索到Wi-Fi中的其他设备,但是在这设置里面却可以。因此,找来其Android8.0的源代码作为参考,并成功解决问题。
源代码位置:
我们可以通过系统的源代码来了解其相应的API的使用。下面是对应的系统源代码的分析:
注册权限 1 2 3 4 5 <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()
中取消广播接收。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION); mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
对上面广播的处理(需要留意的是,这些数据都是从Intent
中取出来的 ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 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)) { 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 wifip2pinfo = (WifiP2pInfo) intent.getParcelableExtra( WifiP2pManager.EXTRA_WIFI_P2P_INFO); if (networkInfo.isConnected()) { if (DBG) Log.d(TAG, "Connected" ); } else if (mLastGroupFormed != true ) { 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 ); } } } };
点击搜索 点击菜单栏上的搜索后,会进行如下操作。然后会接收到相应的广播,刷新是在对相应广播的处理中进行的。
1 2 3 4 5 6 7 8 9 10 11 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()
:
1 2 3 4 5 6 7 8 9 10 11 12 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); }
连接设备或断开连接
连接设备 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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}
修饰的类。我们可以考虑通过反射的方式来近行调用,如下:
1 2 3 4 5 6 7 8 9 10 11 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 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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); } }); } } } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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); } }); } } } };
重命名设备名称 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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发送消息。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 new Thread (new Runnable () { @Override public void run () { try { sendSocket = new Socket (wifip2pinfoG.groupOwnerAddress, 6666 ); OutputStreamWriter osw = new OutputStreamWriter (sendSocket.getOutputStream()); osw.write(getInfo()); osw.flush(); Log.e("TAG" , "info sended" ); sendSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }).start();