[转载]从Android源代码来看WiFi直连
什么是WiFi直連
通俗點(diǎn)說(shuō),它可以不通過(guò)網(wǎng)絡(luò),也不通過(guò)藍(lán)牙,只要兩臺(tái)設(shè)備都支持WiFi直連,打開(kāi)WiFi,不用連接任何WiFi,就可以進(jìn)行信息的傳輸(請(qǐng)忽略下面兩張圖中的WiFi連接標(biāo)志,因?yàn)槠渑cWiFi的連接與否無(wú)關(guān),打開(kāi)就可以)。
在Android的設(shè)置->網(wǎng)絡(luò)與互聯(lián)網(wǎng)->WLAN->WLAN偏好設(shè)置->高級(jí)->WLAN直連中可以找到關(guān)于Wi-Fi直連的設(shè)置,如下:
在參考其它博客時(shí),寫出來(lái)的代碼并不能搜索到Wi-Fi中的其他設(shè)備,但是在這設(shè)置里面卻可以。因此,找來(lái)其Android8.0的源代碼作為參考,并成功解決問(wèn)題。
源代碼位置:
- /packages/apps/Settings/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
- 在線Android8.0 WiFi直連相關(guān)源代碼
我們可以通過(guò)系統(tǒng)的源代碼來(lái)了解其相應(yīng)的API的使用。下面是對(duì)應(yīng)的系統(tǒng)源代碼的分析:
注冊(cè)權(quán)限
<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"/>- 1
- 2
- 3
- 4
- 5
廣播的接收與處理
在onResume()中注冊(cè)廣播接收,在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);- 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
對(duì)上面廣播的處理(需要留意的是,這些數(shù)據(jù)都是從Intent中取出來(lái)的):
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic 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中取出了列表數(shù)據(jù)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// 這是最神奇的地方,沒(méi)有網(wǎng)絡(luò)的連接,卻得到了IP// 然后我們可以通過(guò)這個(gè)IP,與GO進(jìn)行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 eventstartSearch();}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
- 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
點(diǎn)擊搜索
點(diǎn)擊菜單欄上的搜索后,會(huì)進(jìn)行如下操作。然后會(huì)接收到相應(yīng)的廣播,刷新是在對(duì)相應(yīng)廣播的處理中進(jìn)行的。
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);}});} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
在對(duì)廣播的處理中,設(shè)備變化的處理主要是靠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); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
連接設(shè)備或斷開(kāi)連接
- 連接設(shè)備
- 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
對(duì)這段代碼中,有一個(gè)使用了android.os.SystemProperties這個(gè){@hide}修飾的類。我們可以考慮通過(guò)反射的方式來(lái)近行調(diào)用,如下:
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
- 斷開(kāi)連接
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 取消已發(fā)送的邀請(qǐng)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
重命名設(shè)備名稱
mRenameListener = new OnClickListener() {@Overridepublic 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();}});}}} };- 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
如何進(jìn)行信息的傳輸?
對(duì)于GO來(lái)說(shuō),當(dāng)與GC(Group Client)連接完成后,也就是接收到WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION廣播后,需要充當(dāng)?shù)慕巧欠?wù)器,所以可以利用Java中socket通信中的SocketServer來(lái)阻塞當(dāng)前的線程(子),監(jiān)聽(tīng)所受到的socket。如下:
if (wifip2pinfoG.isGroupOwner) { // 充當(dāng)服務(wù)器new Thread(new Runnable() {// 不能阻塞主線程@Override // 同時(shí)也不允許在主線程中進(jìn)行網(wǎng)絡(luò)通信public void run() { // 所以開(kāi)啟子線程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() {@Overridepublic void run() {Toast.makeText(context, log, Toast.LENGTH_SHORT).show();}});}} catch (IOException e) {e.printStackTrace();}}}).start();Toast.makeText(context, "服務(wù)器已啟動(dòng)", Toast.LENGTH_SHORT).show(); } else { // 充當(dāng)客戶端Toast.makeText(context, "可發(fā)送消息", Toast.LENGTH_SHORT).show(); }- 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
對(duì)GC來(lái)說(shuō),當(dāng)與GO連接好了之后,即可發(fā)送給GO發(fā)送消息。如下:
new Thread(new Runnable() {// 不能在主線程中進(jìn)行網(wǎng)絡(luò)通信,需要子線程@Overridepublic void run() {try {sendSocket = new Socket(wifip2pinfoG.groupOwnerAddress, 6666);OutputStreamWriter osw = new OutputStreamWriter(sendSocket.getOutputStream());// getInfo()是一個(gè)輸入框,沒(méi)有輸入時(shí)默認(rèn)返回helloosw.write(getInfo());osw.flush();Log.e("TAG", "info sended");// 注意這里,需要及時(shí)關(guān)閉socketsendSocket.close();} catch (IOException e) {e.printStackTrace();}} }).start();- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
總結(jié)
以上是生活随笔為你收集整理的[转载]从Android源代码来看WiFi直连的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: DIB函数注释
- 下一篇: AI on Android:安卓平台上的