1. 程式人生 > >Android WiFi開發教程(二)——WiFi的搜尋和連線

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熱點資料傳輸