Android Wifi连接总结

6,620 阅读5分钟

一、前言

最近在做一个wifi相关的项目,因此去了解了官方文档和相关技术资料,整理了一下目前wifi连接的可行性和办法,包括扫描、连接、状态监听。

二、使用方法

目前安卓10开始官方已经弃用原来的连接wifi了,改用对等连接:developer.android.com/guide/topic… 而这个对等连接其实就是APP内部连接到该wifi,但是手机本身是保持原来连接的wifi的,所以原来那种修改系统wifi连接的已经弃用了,想继续使用,需要把项目targetSdkVersion修改为小于29。

1.wifi管理类

    object WifiUtils {
      val mWifiManager by lazy {
          WiFiMoneyApplication.getApplication ()?.applicationContext?.getSystemService(Context.WIFI_SERVICE) as WifiManager
      }
    }

开启wifi

    fun openWifi() {
        mWifiManager.isWifiEnabled = true
    }

连接wifi

    @SuppressLint("MissingPermission")
    fun connectWifi(context: Context, scanResults: List<ScanResult>,  ssid: String, connect: (success: Boolean, scanResult: ScanResult) -> Unit) {
        if (!mWifiManager.isWifiEnabled) {
            return
        }
        val scanResult = scanResults.singleOrNull { it.SSID == ssid }
        if (scanResult != null) {
            val config = mWifiManager.configuredNetworks.singleOrNull { it.SSID.replace("\"", "") == ssid }
            if (config != null && config.status != WifiConfiguration.Status.DISABLED) {
                if (BuildConfig.LOG_DEBUG) {
                    Log.d(TAG, "找到了历史wifi:${scanResult.SSID}")
                }
                connect.invoke(mWifiManager.enableNetwork(config.networkId, true), scanResult)
            } else {
                val type = getCipherType(scanResult.capabilities)
                if (type == WifiCapability.WIFI_CIPHER_NO_PASS) {
                    // 没找到历史wifi
                    val padWifiNetwork =
                            createWifiConfig(
                                scanResult.SSID,
                                type = getCipherType(scanResult.capabilities))
                    val netId = mWifiManager.addNetwork(padWifiNetwork)
                    if (BuildConfig.LOG_DEBUG) {
                        Log.d(TAG, "不需要密码连接wifi:${scanResult.SSID}")
                    }
                    connect.invoke(mWifiManager.enableNetwork(netId, true), scanResult)
                } else {
                    if (BuildConfig.LOG_DEBUG) {
                        Log.d(TAG, "需要密码连接wifi:${scanResult.SSID}")
                    }
                    connect.invoke(false, scanResult)
                }
            }
        } else {
            if (BuildConfig.LOG_DEBUG) {
                Log.d(TAG, "connectWifi 没有找到")
            }
        }
    }
    
    @SuppressLint("MissingPermission")
    fun createWifiConfig(
        ssid: String,
        password: String = "",
        type: WifiCapability
                                ): WifiConfiguration {
        //初始化WifiConfiguration
        val config = WifiConfiguration()
        config.allowedAuthAlgorithms.clear()
        config.allowedGroupCiphers.clear()
        config.allowedKeyManagement.clear()
        config.allowedPairwiseCiphers.clear()
        config.allowedProtocols.clear()

        //指定对应的SSID
        config.SSID = "\"" + ssid + "\""

        //如果之前有类似的配置
        val tempConfig = mWifiManager.configuredNetworks.singleOrNull { it.BSSID == "\"$ssid\"" }
        if (tempConfig != null) {
            //则清除旧有配置  不是自己创建的network 这里其实是删不掉的
            val isDisable = mWifiManager.disableNetwork(tempConfig.networkId)
            val isRemove = mWifiManager.removeNetwork(tempConfig.networkId)
            val isSave = mWifiManager.saveConfiguration()
            if (BuildConfig.LOG_DEBUG) {
                Log.d(TAG, "清除wifi配置:${tempConfig.SSID + (isDisable && isRemove && isSave)}")
            }
        }

        //不需要密码的场景
        if (type == WifiCapability.WIFI_CIPHER_NO_PASS) {
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
            //以WEP加密的场景
        } else if (type == WifiCapability.WIFI_CIPHER_WEP) {
            config.hiddenSSID = true
            config.wepKeys[0] = "\"" + password + "\""
            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN)
            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED)
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
            config.wepTxKeyIndex = 0
            //以WPA加密的场景
        } else if (type == WifiCapability.WIFI_CIPHER_WPA) {
            config.preSharedKey = "\"" + password + "\""
            config.hiddenSSID = true
            config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN)
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP)
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK)
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP)
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP)
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP)
            config.status = WifiConfiguration.Status.ENABLED
        }
        return config
    }
    
    fun getCipherType(capabilities: String): WifiCapability {
        return when {
            capabilities.contains("WEB") -> {
                WifiCapability.WIFI_CIPHER_WEP
            }
            capabilities.contains("PSK") -> {
                WifiCapability.WIFI_CIPHER_WPA
            }
            capabilities.contains("WPS") -> {
                WifiCapability.WIFI_CIPHER_NO_PASS
            }
            else -> {
                WifiCapability.WIFI_CIPHER_NO_PASS
            }
        }
    }

    enum class WifiCapability {
        WIFI_CIPHER_WEP, WIFI_CIPHER_WPA, WIFI_CIPHER_NO_PASS
    }

外部调用

    fun connectWifi(item: ScanResult) {
        if (item.BSSID == WifiUtils.mWifiManager.connectionInfo.bssid) {
            ToastUtils.showToast(context, getString(R.string.wifi_home_connect_error))
            return
        }

        if (BuildConfig.LOG_DEBUG) {
            Log.d(TAG, "开始切换wifi")
        }

        WifiUtils.connectWifi(context!!, mWifiList, item.SSID) { success, scanResult ->
            if (success) {
                // 这个时候找到wifi,开始连接
            } else {
                // 未成功找到wifi添加,尝试输入密码
                activity?.let { activity ->
                    InputPasswordDialog(activity, scanResult.SSID) { password ->
                        val padWifiNetwork =
                            WifiUtils.createWifiConfig(
                                scanResult.SSID,
                                password,
                                WifiUtils.getCipherType(scanResult.capabilities)
                            )

                        val netId = WifiUtils.mWifiManager.addNetwork(padWifiNetwork)
                        val success2 = WifiUtils.mWifiManager.enableNetwork(netId, true)

                        if (success2) {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "这个时候找到wifi,开始连接:${success2}")
                            }
                            startConnectAni(item)
                        } else {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "创建连接失败")
                            }
                            ToastUtils.showToast(context!!, "创建连接失败")
                        }
                    }
                }?.show()
            }
        }
    }

wifi强度获取

    fun getNetworkWifiLevel(wifiInfo: ScanResult): Int {
        //获得信号强度值
        val level = wifiInfo.level
        //根据获得信号的强度发送信息
        return if (level <= 0 && level >= -50) { //最强
            5
        }
        else if (level < -50 && level >= -70) { //较强
            4
        }
        else if (level < -70 && level >= -80) { //较弱
            3
        }
        else if (level < -80 && level >= -100) { //微弱
            2
        }
        else {
            1
        }
    }

2.wifi连接状态监听

    private fun registerWifiStatusListener() {
        val filter = IntentFilter()
        filter.addAction(SUPPLICANT_STATE_CHANGED_ACTION)// wifi连接结果
        filter.addAction(WIFI_STATE_CHANGED_ACTION) // wifi开启关闭状态
        filter.addAction(NETWORK_STATE_CHANGED_ACTION)// wifi连接状态
        filter.addAction(SCAN_RESULTS_AVAILABLE_ACTION)// 监听wifi列表变化(开启一个热点或者关闭一个热点)
        filter.addAction("android.net.conn.CONNECTIVITY_CHANGE")// 网络类型切换
        activity?.application?.registerReceiver(mWifiReceiver, filter)
    }
    
    private val mWifiReceiver: BroadcastReceiver by lazy {
        object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent) {
                if (intent.action === WIFI_STATE_CHANGED_ACTION) {
                    when (intent.getIntExtra(EXTRA_WIFI_STATE, WIFI_STATE_UNKNOWN)) {
                        // 已关闭
                        WIFI_STATE_DISABLED -> {
                            updateWifiStatus(6)
                        }
                        // 关闭中
                        WIFI_STATE_DISABLING -> {}
                        // 可用
                        WIFI_STATE_ENABLED -> {}
                        // 正在开启
                        WIFI_STATE_ENABLING -> {
                            updateWifiStatus(5)
                        }
                        // 未知
                        WIFI_STATE_UNKNOWN -> {
                            updateWifiStatus(1)
                        }
                    }
                } else if (NETWORK_STATE_CHANGED_ACTION == intent.getAction()) {
                    val info: NetworkInfo = intent.getParcelableExtra(EXTRA_NETWORK_INFO)
                    if (NetworkInfo.State.DISCONNECTED == info.state) { // wifi没连接上,连接已断开
                        if (BuildConfig.LOG_DEBUG) {
                            Log.d(TAG, "连接已断开")
                        }
                    } else if (NetworkInfo.State.CONNECTED == info.state) { //wifi连接上了
                        if (BuildConfig.LOG_DEBUG) {
                            Log.d("TAG, "wifi连接上了")
                        }
                        refreshWifiList()
                    } else if (NetworkInfo.State.CONNECTING == info.state) { // 正在连接
                        if (BuildConfig.LOG_DEBUG) {
                            Log.d(TAG, "wifi正在连接")
                        }
                    }
                } else if (SCAN_RESULTS_AVAILABLE_ACTION == intent.action) {
                    val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
                    if (success) {
                      scanSuccess()
                    } else {
                      scanFailure()
                    }
                    // wifi连接结果通知   WIFI连接请求状态发生改变
                } else if (SUPPLICANT_STATE_CHANGED_ACTION == intent.action) {

                    // 密码错误
                    val error = intent.getIntExtra(EXTRA_SUPPLICANT_ERROR, -1)
                    if (error == ERROR_AUTHENTICATING) {
                        if (BuildConfig.LOG_DEBUG) {
                            Log.d(TAG, "获取连接状态:密码错误")
                        }
                        // todo 连接失败
                        return
                    }

                    // 获取连接状态
                    val supplicantState: SupplicantState =
                        intent.getParcelableExtra(EXTRA_NEW_STATE)
                    when (supplicantState) {
                        // 成功
                        SupplicantState.COMPLETED -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:成功")
                            }
                        	// todo 连接成功
                        }
                        // 不活跃的
                        SupplicantState.INACTIVE -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:不活跃的")
                            }
                        	// todo 连接失败
                        }
                        // 接口禁用
                        SupplicantState.INTERFACE_DISABLED -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:接口禁用")
                            }
                        }
                        SupplicantState.DISCONNECTED -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:断开连接")
                            }
                        }
                        SupplicantState.SCANNING -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:正在扫描")
                            }
                        }
                        SupplicantState.AUTHENTICATING -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:正在验证")
                            }
                        }
                        SupplicantState.ASSOCIATING -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:正在关联")
                            }
                        }
                        SupplicantState.ASSOCIATED -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:已经关联")
                            }
                        }
                        SupplicantState.FOUR_WAY_HANDSHAKE -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:四次握手")
                            }
                        }
                        SupplicantState.GROUP_HANDSHAKE -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:组握手")
                            }
                        }
                        SupplicantState.DORMANT -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:休眠")
                            }
                        }
                        SupplicantState.UNINITIALIZED -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:未初始化")
                            }
                        }
                        SupplicantState.INVALID -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "获取连接状态:无效的")
                            }
                        }
                        else -> {
                            if (BuildConfig.LOG_DEBUG) {
                                Log.d(TAG, "wifi连接结果通知")
                            }
                        }
                    }

                } else if ("android.net.conn.CONNECTIVITY_CHANGE" == intent.action) {
                    if (BuildConfig.LOG_DEBUG) {
                        Log.d(TAG, "网络类型切换了")
                    }
                    updateNetWorkType()
                }
            }
        }
    }

3.wifi相关权限申请

    RxPermissions(this).request(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.CHANGE_WIFI_STATE,
                Manifest.permission.ACCESS_WIFI_STATE
            ).subscribe {
                if (!it) {
                    //没权限
                }
            }

4.wifi列表更新

    fun refreshWifiList() {
        if (!WifiUtils.mWifiManager.isWifiEnabled) {
            return
        }

        mWifiList.clear()

        // 原始wifi列表
        val originList = WifiUtils.mWifiManager.scanResults
        // 当前wifi
        val currentWifi = originList.singleOrNull {
            it.BSSID == WifiUtils.mWifiManager.connectionInfo.bssid &&
                    WifiUtils.mWifiManager.connectionInfo.supplicantState == SupplicantState.COMPLETED
        }

        // 添加wifi到列表
        for ((i, _) in originList.withIndex()) {

            // 该热点SSID是否已在列表中
            val position: Int = getItemPosition(mWifiList, originList[i])
            // 过滤没有名字的,没有意义
            if (originList[i].SSID.isNotEmpty()) {
                if (position != -1) { // 已在列表
                    // 相同SSID热点,取信号强的
                    if (mWifiList[position].level < originList[i].level) {
                        mWifiList.removeAt(position)
                        mWifiList.add(position, originList[i])
                    }
                } else {
                    mWifiList.add(originList[i])
                }
            }

            // 按信号强度排序
            mWifiList.sortWith(Comparator { t1, t2 -> t2.level - t1.level })
        }

        // 置顶当前连接的wifi
        var p = -1
        currentWifi?.let {
            for ((i, value) in mWifiList.withIndex()) {
                if (currentWifi.BSSID == value.BSSID) {
                    p = i
                }
            }

            updateWifiName()
        }

        if (p != -1 && currentWifi != null) {
            mWifiList.removeAt(p)
            mWifiList.add(0, currentWifi)
            mWifiAdapter.connectPosition = 0
        } else {
            mWifiAdapter.connectPosition = -1
        }
        mWifiAdapter.notifyDataSetChanged()

        // 处于连接中的wifi,成功了才切换状态
        if (mLastStatus != 2 || (mLastStatus == 2 && currentWifi != null)) {
            if (mWifiList.isNotEmpty()) {
                updateWifiStatus(1)
            } else {
                updateWifiStatus(4)
            }
        }

        // 刷新列表
        updateListOpenOrNot()
    }

5.正在使用的网络类型判断(在监听关闭wifi网络的时候,调用这个依然返回有网络)

object NetworkUtils {

    const val NETWORK_NOT_DEFINE = -2 // 没有定义

    const val NETWORK_NONE = -1 // 没有网络连接

    const val NETWORK_WIFI = 1 // wifi连接

    const val NETWORK_2G = 2 // 2G

    const val NETWORK_3G = 3 // 3G

    const val NETWORK_4G = 4 // 4G

    const val NETWORK_5G = 5 // 5G

    const val NETWORK_MOBILE = 0 // 手机流量

    /**
     * 获取当前网络连接的类型
     *
     * @param context context
     * @return int
     */
    fun getNetworkState(context: Context): Int {
        val connManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        
        // 获取网络类型,如果为空,返回无网络
        val activeNetInfo = connManager.activeNetworkInfo
        if (activeNetInfo == null || !activeNetInfo.isAvailable) {
            return NETWORK_NONE
        }
        // 判断是否为WIFI
        val wifiInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
        if (null != wifiInfo) {
            val state = wifiInfo.state
            if (null != state) {
                if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) {
                    return NETWORK_WIFI
                }
            }
        }
        // 若不是WIFI,则去判断是2G、3G、4G网
        val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        val networkType = telephonyManager.networkType
        return when (networkType) {
            TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_EDGE, TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyManager.NETWORK_TYPE_IDEN -> NETWORK_2G
            TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_HSDPA, TelephonyManager.NETWORK_TYPE_HSUPA, TelephonyManager.NETWORK_TYPE_HSPA, TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyManager.NETWORK_TYPE_HSPAP -> NETWORK_3G
            TelephonyManager.NETWORK_TYPE_LTE -> NETWORK_4G
            TelephonyManager.NETWORK_TYPE_NR -> NETWORK_5G
            else -> NETWORK_MOBILE
        }
    }

    /**
     * 是否正在用手机流量
     */
    fun isMobileNetwork (context: Context): Boolean {
        val a = getNetworkState(context)

        if (BuildConfig.LOG_DEBUG) {
            Log.d(TAG, "是否正在用手机流量 getNetworkState:${a}")
        }
        return a != NETWORK_NONE && a != NETWORK_NOT_DEFINE && a != NETWORK_WIFI
    }
}

6.wifi相关api需要定位功能,需要判断用户有没有开定位


    /**
     * 手机是否开启位置服务
     */
    fun isLocServiceEnable(context: Context): Boolean {
        val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        val gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
        val network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
        return gps || network
    }

7.android10以上新功能,也列出来吧。

P2P连接Wifi就是对等连接,在APP内部会出现对话框,请求某个wifi连接,仅APP内部使用

    // Android10以上 通过P2P连接Wifi
    @RequiresApi(Build.VERSION_CODES.Q)
    fun connectByP2P(context: Context, scanResult: ScanResult, password: String, successListener: ((Network?) -> Unit)? = null,  failListener: (() -> Unit)? = null) {
    
        val specifier = WifiNetworkSpecifier.Builder()
                .setSsidPattern(PatternMatcher(scanResult.SSID, PatternMatcher.PATTERN_PREFIX))
                .setWpa2Passphrase(password)
                .build()
        val request =
                NetworkRequest.Builder()
                        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                        .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                        .setNetworkSpecifier(specifier)
                        .build()

        val connectivityManager =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        val networkCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network?) {
                connectivityManager.bindProcessToNetwork(network)
                successListener?.invoke(network)
            }

            override fun onUnavailable() {
                failListener?.invoke()
            }
        }

        connectivityManager.requestNetwork(request, networkCallback)
//        使用完毕后调用这个方法 connectivityManager.unregisterNetworkCallback(networkCallback)

    }

这个是给系统提建议,个人感觉非常鸡肋

    // Android10以上,通过suggestion连接WIFI
    @RequiresApi(Build.VERSION_CODES.Q)
    private fun connectBySug(ssid: String, password: String) {
        val suggestion = WifiNetworkSuggestion.Builder()
                .setSsid(ssid)
                .setWpa2Passphrase(password)
                .setIsAppInteractionRequired(true) // Optional (Needs location permission)
                .build()
        val suggestionsList = listOf(suggestion)
        //wifiManager.removeNetworkSuggestions(suggestionsList)
        val status = wifiManager.addNetworkSuggestions(suggestionsList)
        if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {

        }
        val intentFilter = IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);
        val broadcastReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                if (!intent.action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
                    return
                }
            }
        };
        context.registerReceiver(broadcastReceiver, intentFilter);

    }

总结:在开发过程中发现wifi的监听真的非常多,估计并没有列出完全监听,欢迎大家可以补充,有问题可以评论讨论。