Android WiFi開發教程(二)——WiFi的搜索和連接

在上一篇中我們介紹了WiFi熱點的創建和關閉,如果你還沒閱讀過,建議先閱讀上一篇文章Android WiFi開發教程(一)——WiFi熱點的創建與關閉。 本章節主要繼續介紹WiFi的搜索和連接。

WiFi的搜索

  /* 搜索wifi熱點
     */
    private void search() {
        if (!wifiManager.isWifiEnabled()) {
            //開啓wifi
            wifiManager.setWifiEnabled(true);
        }
        wifiManager.startScan();
    }

我們在開始搜索WiFi之前確保當前WiFi功能是處於開啓狀態。如果未開啓,通過調用WifiManager的setWifiEnabled(boolean enable)去開啓。之後調用startScan()就開始掃描附近的WiFi了。而獲取掃描的結果我們就需要創建一個廣播接收者來處理。

private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                // wifi已成功掃描到可用wifi。
                List<ScanResult> scanResults = wifiManager.getScanResults();
                wifiListAdapter.clear();
                wifiListAdapter.addAll(scanResults);
            } 
    };

系統在掃描結束後,會發出WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的廣播,當我們的接收者接收到這個廣播的時候,通過WifiManager的getScanResults()就能獲取到掃描結果的集合了。ScanResult保存着每一個WiFi的信息。這裏我將這個集合設置到Adapter中,並在列表中展示出來。下面是Apater中主要的代碼:

 @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = mInflater.inflate(mResource, parent, false);
        }

        TextView name = (TextView) convertView.findViewById(R.id.wifi_name);
        TextView signl = (TextView) convertView.findViewById(R.id.wifi_signal);

        ScanResult scanResult = getItem(position);
        name.setText(scanResult.SSID);

        int level = scanResult.level;
        if (level <= 0 && level >= -50) {
            signl.setText("信號很好");
        } else if (level < -50 && level >= -70) {
            signl.setText("信號較好");
        } else if (level < -70 && level >= -80) {
            signl.setText("信號一般");
        } else if (level < -80 && level >= -100) {
            signl.setText("信號較差");
        } else {
            signl.setText("信號很差");
        }

        return convertView;
    }

可以看出列表展示的數據也是比較簡單,只有WiFi的名稱和信號強度,這兩個數據也是平時用得比較多的。獲取到掃描結果後,我們就可以處理連接的邏輯了。

WiFi的連接

WiFi的連接相當於搜索就要複雜一些。首先給列表項設置點擊事件,獲取對應的ScanResult。

   listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                wifiManager.disconnect();
                final ScanResult scanResult = wifiListAdapter.getItem(position);
                 String capabilities = scanResult.capabilities;
                int type = WIFICIPHER_WPA;
                if (!TextUtils.isEmpty(capabilities)) {
                    if (capabilities.contains("WPA") || capabilities.contains("wpa")) {
                        type = WIFICIPHER_WPA;
                    } else if (capabilities.contains("WEP") || capabilities.contains("wep")) {
                        type = WIFICIPHER_WEP;
                    } else {
                        type = WIFICIPHER_NOPASS;
                    }
                }
                config = isExsits(scanResult.SSID);
        });

獲取到ScanResult後我們通過他的capabilities屬性判斷WiFi的加密方式。接着通過isExsits(String SSID)方法判斷系統是否保存着當前WiFi的信息。

private WifiConfiguration isExsits(String SSID) {
        List<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks();
        for (WifiConfiguration existingConfig : existingConfigs) {
            if (existingConfig.SSID.equals("\"" + SSID + "\"")) {
                return existingConfig;
            }
        }
        return null;
    }

如果之前連接過,則返回WiFi的配置信息,否則返回空對象。然後接着處理連接的邏輯

if (config == null) {
                    if (type != WIFICIPHER_NOPASS) {//需要密碼
                        final EditText editText = new EditText(MainActivity.this);
                        final int finalType = type;
                        new AlertDialog.Builder(MainActivity.this).setTitle("請輸入Wifi密碼").setIcon(
                                android.R.drawable.ic_dialog_info).setView(
                                editText).setPositiveButton("確定", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Log.w("AAA", "editText.getText():" + editText.getText());
                                config = createWifiInfo(scanResult.SSID, editText.getText().toString(), finalType);
                                connect(config);
                            }
                        })
                                .setNegativeButton("取消", null).show();
                        return;
                    } else {
                        config = createWifiInfo(scanResult.SSID, "", type);
                        connect(config);
                    }
                } else {
                    connect(config);
                }

當沒有獲取到所要連接WiFi的配置信息時,我們就需要用到前面獲取到的加密方式判斷是否需要輸入密碼。如果加密方式爲WAP或WEP時,則彈出提示框提示用戶輸入WiFi密碼。用戶輸入密碼後再調用connect(WifiConfiguration config)方法

如果可以獲取到所要連接WiFi的配置信息,則直接調用connect(WifiConfiguration config)。

private void connect(WifiConfiguration config) {
        int wcgID = wifiManager.addNetwork(config);
        wifiManager.enableNetwork(wcgID, true);
    }

直接調用WifiManger的addNetwork方法,將配置信息傳進去後,會創建一個新的網絡描述的身份並返回回來,如果返回來是-1,則表示創建失敗。獲取到身份後,調用enableNetwork方法就能開始連接WiFi了。到了這裏,我們連接部分就完成了一半,接下來需要繼續處理WiFi連接過程中返回來的狀態。這裏同樣我們是需要用到廣播接收者來處理。

if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
  NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);

                if (info.getState().equals(NetworkInfo.State.DISCONNECTED)) {
                    text_state.setText("連接已斷開");
                } else if (info.getState().equals(NetworkInfo.State.CONNECTED)) {
                    WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
                    final WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                    text_state.setText("已連接到網絡:" + wifiInfo.getSSID());

                    }
                } else {
                    NetworkInfo.DetailedState state = info.getDetailedState();
                    if (state == state.CONNECTING) {
                        text_state.setText("連接中...");
                    } else if (state == state.AUTHENTICATING) {
                        text_state.setText("正在驗證身份信息...");
                    } else if (state == state.OBTAINING_IPADDR) {
                        text_state.setText("正在獲取IP地址...");
                    } else if (state == state.FAILED) {
                        text_state.setText("連接失敗");
                    }
                }

            }

上面是廣播接收者中的關鍵代碼。WiFi在連接的過程中系統會發出WifiManager.NETWORK_STATE_CHANGED_ACTION的廣播,當接收者接收到這條廣播時,獲取NetworkInfo的state來判斷當前的連接狀態。狀態值分別代表如下

NetworkInfo.State.DISCONNECTED //連接已斷開
NetworkInfo.State.CONNECTED //已成功連接

除了這兩個狀態之外,這裏還判斷了其他狀態

NetworkInfo.DetailedState state = info.getDetailedState();
                    if (state == state.CONNECTING) {
                        text_state.setText("連接中...");
                    } else if (state == state.AUTHENTICATING) {
                        text_state.setText("正在驗證身份信息...");
                    } else if (state == state.OBTAINING_IPADDR) {
                        text_state.setText("正在獲取IP地址...");
                    } else if (state == state.FAILED) {
                        text_state.setText("連接失敗");
                    }

DetailedState中包含了很多連接狀態的信息,這裏只對部分狀態進行處理,其他狀態值解析具體如下

IDLE:空閒
SCANNING:正在掃描
CONNECTING:連接中
AUTHENTICATING:正在進行身份驗證
OBTAINING_IPADDR:正在獲取Ip地址
CONNECTED:已連接
SUSPENDED:已暫停
DISCONNECTING:正在斷開連接
DISCONNECTED:已斷開
FAILED:失敗
BLOCKED:已阻止
VERIFYING_POOR_LINK:暫時關閉(網絡狀況不佳)
CAPTIVE_PORTAL_CHECK:判斷是否需要瀏覽器二次登錄

到這裏WiFi連接的邏輯就處理完成了,相對於WiFi熱點的創建和關閉,搜索和連接確實要複雜一些。歡迎閱讀下一篇Android WiFi開發教程(三)——WiFi熱點數據傳輸

發佈了32 篇原創文章 · 獲贊 111 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章