[Android WIFI開發(fā)]

前言

在Android應(yīng)用層的開發(fā)中,使用到wifi相關(guān)知識點(diǎn)的地方并不多,所以之前對wifi開發(fā)并不熟悉,最近接到的兩個硬件項目都有用到wifi并且知識點(diǎn)越來越深入,所以有必要記一下筆記,做一些注釋。

基本使用

1.權(quán)限

Android中要使用系統(tǒng)功能一般都要申請權(quán)限,在6.0上可能還要手動申請權(quán)限,這里wifi需要的權(quán)限有

    <uses-permission android:name="android.permission.CHANGE_NETWORK_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.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> // 需要系統(tǒng)權(quán)限 [定位權(quán)限]

其中在6.0以上設(shè)備,定位權(quán)限需要主動申請,并且如果要獲取掃描wifi列表需要打開系統(tǒng)的定位開關(guān)。

2. WiFi相關(guān)API

WIFI相關(guān)API

ScanResult類用于存放wifi掃描結(jié)果信息,包含ssid(wifi名稱), bssid(網(wǎng)絡(luò)接入點(diǎn)地址),capabilities(加密類型),frequency(傳輸頻率),level(信號強(qiáng)度)等。這里的解釋并不太標(biāo)準(zhǔn),但是對應(yīng)功能很形象,如果你要了解更多,可以去具體的查閱資料。

WifiConfiguration類用于存放wifi的配置信息。包括wifi的密碼,加密類型,網(wǎng)絡(luò)id(用于連接wifi)等。他的幾個子類分別對應(yīng)秘鑰加密方式,安全協(xié)議等,這些在設(shè)置wifi配置的時候會被用到

wifiinfo類用來描述wifi屬性和連接狀態(tài)。暴露了一些方法給開發(fā)者調(diào)用。getBSSID(), getMacAddress(), getIpAddress(),getSSID()等

WifiManager類是framework層暴露的api,用來管理wifi。通過調(diào)用 Context.getSystemService(Context.WIFI_SERVICE)可以得到類的實例。通過他可以得到:1.已經(jīng)配置的網(wǎng)絡(luò)列表。2.當(dāng)前連接的wifi。3.掃描到的wifi。4.以及一些常量表示廣播的意圖等

3. wifi狀態(tài)及開關(guān)

/**
 * 判斷wifi是否打開
 * <p>需添加權(quán)限 {@code <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>}</p>
 *
 * @return {@code true}: 是<br>{@code false}: 否
 */
public static boolean getWifiEnabled() {
    @SuppressLint("WifiManagerLeak")
    WifiManager wifiManager = (WifiManager) Utils.getApp().getSystemService(Context.WIFI_SERVICE);
    return wifiManager.isWifiEnabled();
}

/**
 * 打開或關(guān)閉wifi
 * <p>需添加權(quán)限 {@code <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>}</p>
 *
 * @param enabled {@code true}: 打開<br>{@code false}: 關(guān)閉
 */
public static void setWifiEnabled(final boolean enabled) {
    @SuppressLint("WifiManagerLeak")
    WifiManager wifiManager = (WifiManager) Utils.getApp().getSystemService(Context.WIFI_SERVICE);
    if (enabled) {
        if (!wifiManager.isWifiEnabled()) {
            wifiManager.setWifiEnabled(true);
        }
    } else {
        if (wifiManager.isWifiEnabled()) {
            wifiManager.setWifiEnabled(false);
        }
    }
}

4. 掃描wifi

 /**
 *
 * 獲取WIFI列表
 * <p>需要權(quán)限{@code <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>}</p>
 * <p>注意Android6.0上需要主動申請定位權(quán)限,并且打開定位開關(guān)</p>
 *
 * @param context 上下文
 * @return wifi列表
 */
public static List<ScanResult> getWifiList(Context context) {
    WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    List<ScanResult> scanResults = wm.getScanResults();

    Collections.sort(scanResults, new Comparator<ScanResult>() {
        @Override
        public int compare(ScanResult scanResult1, ScanResult scanResult2) {
            return scanResult2.level - scanResult1.level;
        }
    });
    return scanResultsCopy;
}

 /**
 * 獲取當(dāng)前鏈接的WiFi信息
 *
 * @param context 上下文
 * @return 當(dāng)前wifi數(shù)據(jù)
 */
public static WifiInfo getCurrentWifi (Context context) {
    WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    return wm.getConnectionInfo();
}


public static String getWifiEncryptTypeStr (String capabilitie) {
    if (TextUtils.isEmpty(capabilitie)) return null;

    String encryptType;

    if (capabilitie.contains("WPA") && capabilitie.contains("WPA2")) {
        encryptType = "WPA/WPA2 PSK";
    } else if (capabilitie.contains("WPA2")) {
        encryptType = "WPA2 PSK";
    } else if (capabilitie.contains("WPA")) {
        encryptType = "WPA PSK";
    } else if (capabilitie.contains("WEP")) {
        encryptType = "WEP";
    } else {
        encryptType = "NONE";
    }

    return encryptType;
}

/**
 * wifi加密方式有5種
 * 0 - WPA/WPA2 PSK
 * 1 - WPA2 PSK
 * 2 - WPA PSK
 * 3 - WEP
 * 4 - NONE
 * @param capabilitie
 * @return
 */
public static int getWifiEncryptType (String capabilitie) {
    if (TextUtils.isEmpty(capabilitie)) return -1;

    int encryptType;

    if (capabilitie.contains("WPA") && capabilitie.contains("WPA2")) {
        encryptType = 0;
    } else if (capabilitie.contains("WPA2")) {
        encryptType = 1;
    } else if (capabilitie.contains("WPA")) {
        encryptType = 2;
    } else if (capabilitie.contains("WEP")) {
        encryptType = 3;
    } else {
        encryptType = 4;
    }

    return encryptType;
}

5. 連接wifi

 /**
 * @des 連接已經(jīng)保存過配置的wifi
 * @param context
 * @param ssid
 */
public static void connectWifi (Context context, String ssid) {

    Log.d(TAG, "connectWifi: 去連接wifi: " + ssid);

    if (!getWifiEnabled()) return;

    WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    WifiConfiguration wc = new WifiConfiguration();

    wc.SSID = "\"" + ssid + "\"";

    WifiConfiguration configuration = getWifiConfig(context, ssid);
    if (configuration != null) {
        wm.enableNetwork(configuration.networkId, true);
    }

}

/**
 * @des 連接沒有配置過的wifi
 * @param context
 * @param ssid
 * @param password
 * @param encryptType
 */
public static void connectWifi (Context context, String ssid, String password, int encryptType) {

    Log.d(TAG, "connectWifi: 去連接wifi: " + ssid);

    if (!getWifiEnabled()) return;

    WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    WifiConfiguration wc = new WifiConfiguration();
    wc.allowedAuthAlgorithms.clear();
    wc.allowedGroupCiphers.clear();
    wc.allowedKeyManagement.clear();
    wc.allowedPairwiseCiphers.clear();
    wc.allowedProtocols.clear();

    wc.SSID = "\"" + ssid + "\"";

    WifiConfiguration configuration = getWifiConfig(context, ssid);
    if (configuration != null) {
        wm.removeNetwork(configuration.networkId);
    }

    switch (encryptType) {
        case 4://不加密
            wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
            break;

        case 3://wep加密
            wc.hiddenSSID = true;
            wc.wepKeys[0] = "\"" + password +"\"";
            wc.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
            wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
            wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
            wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);

            break;
        case 0: //wpa/wap2加密
        case 1: //wpa2加密
        case 2: //wpa加密

            wc.preSharedKey = "\"" + password + "\"";
            wc.hiddenSSID = true;
            wc.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
            wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            wc.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            wc.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
            wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
            wc.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
            wc.status = WifiConfiguration.Status.ENABLED;
            break;
    }

    int network = wm.addNetwork(wc);
    wm.enableNetwork(network, true);
}

public static void disConnectWifi (Context context, int networkId) {
    WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    wm.disableNetwork(networkId);
    wm.disconnect();
}

6. 獲取和清除WIFI配置

/**
 * @des 清除wifi配置信息
 * @param context
 * @param ssid
 */
public static void clearWifiInfo(Context context, String ssid) {

    Log.d(TAG, "clearWifiInfo: 清除WIFI配置信息: " + ssid);

    WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

    String newSSID = "\"" + ssid + "\"";

    if (!(ssid.startsWith("\"") && ssid.endsWith("\""))) {
        newSSID = "\"" + ssid + "\"";
    } else {
        newSSID = ssid;
    }

    WifiConfiguration configuration = getWifiConfig(context, newSSID);
    configuration.allowedAuthAlgorithms.clear();
    configuration.allowedGroupCiphers.clear();
    configuration.allowedKeyManagement.clear();
    configuration.allowedPairwiseCiphers.clear();
    configuration.allowedProtocols.clear();

    if (configuration != null) {

        wm.removeNetwork(configuration.networkId);
        wm.saveConfiguration();
    }
}

public static WifiConfiguration getWifiConfig (Context context, String ssid) {

    Log.d(TAG, "getWifiConfig: 獲取wifi配置信息: " + ssid);

    if (TextUtils.isEmpty(ssid)) return null;

    String newSSID;

    if (!(ssid.startsWith("\"") && ssid.endsWith("\""))) {
        newSSID = "\"" + ssid + "\"";
    } else {
        newSSID = ssid;
    }

    WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    List<WifiConfiguration> configuredNetworks = wm.getConfiguredNetworks();

    for (WifiConfiguration configuration : configuredNetworks) {
        if (newSSID.equalsIgnoreCase(configuration.SSID)) {
            return configuration;
        }
    }

    return null;
}

高級用法

1. 監(jiān)聽wifi狀態(tài)的變化并刷新wifi列表和連接狀態(tài)

2. 5Gwfif和2.4Gwifi

踩坑記錄

1. wifi的ssid是帶有"",所以你如果要判空是不行的,并且在連接wifi的時候要手動給ssid加上""。

2. 前文已經(jīng)說了Android6.0以上要想掃描到wifi需要開啟權(quán)限和手機(jī)的定位功能,不然得到的列表為空。

3. wifi的秘鑰安全模式網(wǎng)上并沒有人給出標(biāo)準(zhǔn)的答案,各個手機(jī)廠商的wifi系統(tǒng)上也都不一樣,對于我們開發(fā)者而言,主要有5種方式也可以總結(jié)為3中:NONE(無密碼模式), WEP(安全性較低), WAP PSK, WAP2 PSK, WPA/WPA2 PSK(這三種安全性較高)。需要注意的是通過capabilitie屬性得到的字符串是類似 [WPA-PSK-CCMP+TKIP][WPA2-PSK-CCMP+TKIP][ESS] 這樣的,因為wifi的加密方式是安全模式+密碼加密類型組合的方式。你要通過string.contains()方法解析出其中的安全模式,上文的掃描wifi中有解析的代碼。

4. 掃描列表中ssid可能為空或"",一般要剔除掉,而且wifi列表一般是根據(jù)信號強(qiáng)度排序。也有可能列表中出現(xiàn)同名的wifi,我之前遇到是因為現(xiàn)在的路由器同時支持5G wifi和2.4G wifi,兩個wifi屬于不同的頻段。

5. wifi是可以設(shè)置為隱藏wifi的,設(shè)置為隱藏wifi后不能被掃描到,但是可以通過直接輸入ssid和秘鑰的方式連接上。

后記

Android 6.0 中的 Wifi 連接

WiFi加密方式有哪些?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容