• Android开发 WifiHelp 一个封装好的WiFi功能操作类


    前言

      基于SDK API 28 版本的使用kotlin建造者模式封装,将Wifi常用的搜索,连接,断开等等功能的封装。一个类包含全部内容(包括了数据,广播),已经经过了内存泄漏测试,可以直接将整个类复制黏贴到项目中使用。如果发现bug请在这篇博客中留言。

      转载请注明来源:https://www.cnblogs.com/guanxinjing/p/16307287.html

    使用

        override fun onDestroy() {
            super.onDestroy()
            mWifiHelp.destroy()
        }
    
        override fun onResume() {
            super.onResume()
            if (mWifiHelp.isWifiEnabled()){
                mWifiHelp.scan()
            }
        }
    
        private fun initWifi() {
            mWifiHelp = WifiHelp.Build(this)
                .setWifiStateChangedListener {
                    when (it) {
                        //WiFi已关闭
                        WifiManager.WIFI_STATE_DISABLED -> {
                            mAdapter.refreshWifiEnable(false)
                        }
                        //WiFi已开启
                        WifiManager.WIFI_STATE_ENABLED -> {
                            mAdapter.refreshWifiEnable(true)
    
                        }
                        //WiFi状态未知
                        WifiManager.WIFI_STATE_UNKNOWN -> {
                            mAdapter.refreshWifiEnable(false)
                        }
                    }
                }
                .setNetworkStateChangedListener {
                    mAdapter.refreshConnectWifiStatus(
                        when (it) {
                            NetworkInfo.DetailedState.AUTHENTICATING -> "验证密码"
                            NetworkInfo.DetailedState.CONNECTING -> "正在连接"
                            NetworkInfo.DetailedState.CONNECTED -> ""
                            NetworkInfo.DetailedState.DISCONNECTING -> "正在断开"
                            NetworkInfo.DetailedState.DISCONNECTED -> ""
                            NetworkInfo.DetailedState.FAILED -> "连接失败"
                            else -> ""
                        }
                    )
                }
                .setScanCallback {
                    mAdapter.isShowLoadIcon(false)
                    mAdapter.refreshData(it)
                }
                .setAlreadyConnectionCallback {
                    mAdapter.refreshConnectWifiData(it)
                }
                .setErrorAuthenticating {
                    //密码错误
                    Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
                }
                .build()
        }

    代码

    import android.annotation.SuppressLint
    import android.content.BroadcastReceiver
    import android.content.Context
    import android.content.Intent
    import android.content.IntentFilter
    import android.net.*
    import android.net.wifi.*
    import android.os.Parcelable
    import kotlinx.coroutines.*
    
    
    class WifiHelp {
        private lateinit var mWifiManager: WifiManager
        private var mBuild: Build? = null
        protected var mWiFiChangeReceiver: WiFiChangeReceiver? = WiFiChangeReceiver()
    
        private fun init(build: Build) {
            mWifiManager = build.mContext?.applicationContext?.getSystemService(Context.WIFI_SERVICE) as WifiManager
            mBuild = build
            build.mContext?.let {
                registerReceiver(it)
            }
        }
    
        fun destroy() {
            unregisterReceiver()
            mBuild?.mContext = null
            mBuild?.mWifiStateChangedListener = null
            mBuild?.mNetworkStateChangedListener = null
            mBuild?.mScanCallback = null
            mBuild?.mAlreadyConnectionCallback = null
            mBuild?.mErrorAuthenticating = null
            mBuild = null
        }
    
        fun isWifiEnabled(): Boolean {
            return mWifiManager.isWifiEnabled
        }
    
        fun setWifiEnabled(isEnabled: Boolean) = mWifiManager.setWifiEnabled(isEnabled)
    
    
        /**
         * 搜索WiFi
         */
        fun scan() {
            mWifiManager.isScanAlwaysAvailable
            mWifiManager.startScan()
        }
    
        /**
         * 刷新已经连接的WiFi
         */
        fun refreshConnectWifiData() {
            GlobalScope.launch(Dispatchers.Main) {
                val connectionWifi = withContext(Dispatchers.IO) { getConnectionWifi() }
                mBuild?.mAlreadyConnectionCallback?.invoke(connectionWifi)
            }
        }
    
        fun connect(wifiData: WifiData) {
            connect(wifiData, "")
        }
    
        fun connect(wifiData: WifiData, password: String) {
            val configurationList = mWifiManager.configuredNetworks //已经保存密码的WiFi
            val isSave = configurationList.any {
                removeQuotationMarks(it.SSID) == wifiData.ssid
            }
            if (isSave) {
                //如果是已经保存密码的的WiFi就重新连接
                mWifiManager.enableNetwork(wifiData.netId, true)
            } else {
                val wifiConfiguration = WifiConfiguration()
                //清除一些默认wifi的配置
                wifiConfiguration.allowedAuthAlgorithms.clear()
                wifiConfiguration.allowedGroupCiphers.clear()
                wifiConfiguration.allowedKeyManagement.clear()
                wifiConfiguration.allowedPairwiseCiphers.clear()
                wifiConfiguration.allowedProtocols.clear()
                wifiConfiguration.SSID = addQuotationMarks(wifiData.ssid)
                when (wifiData.capabilities) {
                    WiFiPwdType.ESS -> {
                        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
                    }
                    WiFiPwdType.WAP -> {
                        wifiConfiguration.preSharedKey = addQuotationMarks(password)
                        wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN)
                        wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP)
                        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK)
                        wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP)
                        wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP)
                        wifiConfiguration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP)
                        wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.RSN)
                        wifiConfiguration.allowedProtocols.set(WifiConfiguration.Protocol.WPA)
                        wifiConfiguration.status = WifiConfiguration.Status.ENABLED
                    }
                    WiFiPwdType.WEP -> {
                        wifiConfiguration.wepKeys[0] = addQuotationMarks(password)
                        wifiConfiguration.wepTxKeyIndex = 0;
                        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
                        wifiConfiguration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
                    }
                    WiFiPwdType.WPA2_EAP -> {
                        //不支持企业WiFi密码
                    }
                }
                val netId = mWifiManager.addNetwork(wifiConfiguration)
                mWifiManager.enableNetwork(netId, true)
            }
        }
    
        /**
         * 断开当前连接的网络
         */
        fun disconnect() {
            GlobalScope.launch(Dispatchers.IO) {
                val wifiData = getConnectionWifi() ?: return@launch
                mWifiManager.disconnect()
                mWifiManager.disableNetwork(wifiData.netId)
                mWifiManager.saveConfiguration()
                //自动重连其他可用网络
                mWifiManager.reconnect()
            }
        }
    
        /**
         * 更换密码WiFi
         */
        @SuppressLint("MissingPermission")
        fun changeWifiPwd(wifiData: WifiData, password: String) {
            val wifiConfigurationList = mWifiManager.configuredNetworks
            for (item in wifiConfigurationList) {
                if (item.SSID == null) {
                    continue
                }
                if (item.SSID == addQuotationMarks(wifiData.ssid)) {
                    item.preSharedKey = "\"" + password + "\""
                    mWifiManager.disconnect()
                    val id = mWifiManager.updateNetwork(item)
                    if (id == -1) { //id如果等于 -1 就说明更新失败了
                        return
                    }
                    mWifiManager.enableNetwork(id, true) //启用连接WiFi
                    mWifiManager.reconnect()
                }
            }
        }
    
        /**
         * 移除保存的WiFi
         *
         * @param ssid
         */
        fun removeSaveWifi(wifiData: WifiData) {
            val wifiConfigurationList = mWifiManager.configuredNetworks
            for (item in wifiConfigurationList) {
                if (item.SSID == addQuotationMarks(wifiData.ssid)) {
                    mWifiManager.disconnect()
                    mWifiManager.removeNetwork(item.networkId)
                    mWifiManager.saveConfiguration()
                    mWifiManager.reconnect()
                }
            }
        }
    
        private fun registerReceiver(context: Context) {
            val intentFilter = IntentFilter()
            //当前连接WiFi状态的变化,这个监听是指当前已经连接WiFi的断开与重连的状态
            intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION)
            //WiFi开关状态
            intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION)
            //信号变化
            intentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION)
            //搜索Wifi扫描已完成,并且结果可用
            intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
            //表明与接入点建立连接的状态已经改变。请求者状态是特定于 Wi-Fi 的
            intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)
            context.registerReceiver(mWiFiChangeReceiver, intentFilter)
        }
    
        private fun unregisterReceiver() {
            mBuild?.mContext?.unregisterReceiver(mWiFiChangeReceiver)
        }
    
        /**
         *  WiFi开关状态
         *  WifiManager.WIFI_STATE_DISABLED  //WiFi已关闭
         *  WifiManager.WIFI_STATE_DISABLING //WiFi关闭中
         *  WifiManager.WIFI_STATE_ENABLED //WiFi已开启
         *  WifiManager.WIFI_STATE_ENABLING //WiFi开启中
         *  WifiManager.WIFI_STATE_UNKNOWN //WiFi状态未知
         */
        private fun getWifiState() = mWifiManager.wifiState
    
        @SuppressLint("MissingPermission")
        private suspend fun getScanDevice(): List<WifiData> {
            val list: List<ScanResult> = mWifiManager.scanResults //获取WiFi列表
            val configurationList = mWifiManager.configuredNetworks //已经保存密码的WiFi
            val connectionWifi = getConnectionWifi()
            val wifiList = mutableListOf<WifiData>()
            for (scanResult in list) {
                if (scanResult.SSID.isNullOrEmpty()) {
                    continue
                }
                if (scanResult.SSID == connectionWifi?.ssid) {
                    continue
                }
                val isRepeat = wifiList.any { it.ssid == scanResult.SSID }
                if (!isRepeat) {
                    val configuration = configurationList.find { removeQuotationMarks(it.SSID) == scanResult.SSID }
                    val netId = configuration?.networkId ?: 0
                    val wifiData = WifiData(
                        netId,
                        scanResult.SSID,
                        formatLevel(scanResult.level),
                        scanResult.BSSID,
                        "",
                        securityType(scanResult.capabilities), configuration != null
                    )
                    wifiList.add(wifiData)
                }
            }
            //根据是否已经保存密码与信号强度降序排序
            wifiList.sortWith(kotlin.Comparator { u1, u2 ->
                if (u1.isSavePwd) {
                    u2.isSavePwd.compareTo(u1.isSavePwd)
                } else {
                    u2.rssi.compareTo(u1.rssi)
                }
            })
            return wifiList
        }
    
        /**
         * 获取当前连接的WiFi网络
         */
        private suspend fun getConnectionWifi(): WifiData? {
            val wifiInfo = mWifiManager.connectionInfo ?: return null
            if (wifiInfo.ssid.contains("unknown ssid")){
                //在频繁的切换WiFi与重连WiFi底层会返回 unknown ssid, 这里将其排除掉
                return null
            }
            return WifiData(
                wifiInfo.networkId,
                removeQuotationMarks(wifiInfo.ssid),
                formatLevel(wifiInfo.rssi),
                wifiInfo.bssid,
                getStringId(wifiInfo.ipAddress),
                WiFiPwdType.ESS,
                true
            )
        }
    
        /**
         * 刷新wifi的全部数据(搜索的WiFi与当前连接WiFi数据)
         */
        private fun refreshWifiData() {
            GlobalScope.launch(Dispatchers.IO) {
                val list = getScanDevice()
                val connectionWifi = getConnectionWifi()
                withContext(Dispatchers.Main) {
                    mBuild?.mScanCallback?.invoke(list)
                    mBuild?.mAlreadyConnectionCallback?.invoke(connectionWifi)
                }
            }
        }
    
        /**
         * 移除引号
         *
         * @param content
         * @return
         */
        private fun removeQuotationMarks(content: String): String {
            return content.substring(1, content.length - 1)
        }
    
        /**
         * 添加引号
         *
         * @param content
         * @return
         */
        private fun addQuotationMarks(content: String): String {
            return "\"" + content + "\""
        }
    
        /**
         * 格式化信号
         *
         * WifiInfo.MIN_RSSI = -126;
         * WifiInfo.MAX_RSSI = 200;
         *
         * Quality     Excellent           Good            Fair            Poor
         * dBm        -30 ~ -61        -63 ~ -73     -75 ~ -85        -87 ~ -97
         *
         * @param rssi
         * @return
         */
        private fun formatLevel(rssi: Int): Int {
            return if (rssi < -97) {
                0
            } else if (rssi < -87) {
                1
            } else if (rssi < -75) {
                2
            } else if (rssi < -63) {
                3
            } else {
                4
            }
        }
    
        /**
         * 将idAddress转化成string类型的Id字符串
         *
         * @param idString
         * @return
         */
        private fun getStringId(idString: Int): String {
            val sb = StringBuffer()
            var b = idString shr 0 and 0xff
            sb.append("$b.")
            b = idString shr 8 and 0xff
            sb.append("$b.")
            b = idString shr 16 and 0xff
            sb.append("$b.")
            b = idString shr 24 and 0xff
            sb.append(b)
            return sb.toString()
        }
    
        /**
         *     ESS = 开放网络,不加密,无需密码
         *     WEP = 旧的加密方式,不推荐使用,仅需密码
         *     WAP  = 最常见的加密方式,仅需密码
         *     WPA2-EAP  = 企业加密方式,ID+密码验证
         */
        private fun securityType(capabilities: String?): WiFiPwdType {
            if (capabilities == null || capabilities.isEmpty()) {
                return WiFiPwdType.ESS
            }
            // 如果包含WAP-PSK的话,则为WAP加密方式
            if (capabilities.contains("WPA-PSK") || capabilities.contains("WPA2-PSK")) {
                return WiFiPwdType.WAP
            } else if (capabilities.contains("WPA2-EAP")) {
                return WiFiPwdType.WPA2_EAP
            } else if (capabilities.contains("WEP")) {
                return WiFiPwdType.WEP
            } else if (capabilities.contains("ESS")) {
                // 如果是ESS则没有密码
                return WiFiPwdType.ESS
            }
            return WiFiPwdType.ESS
        }
    
        class Build(context: Context) {
            internal var mScanCallback: ((List<WifiData>?) -> Unit)? = null
            internal var mAlreadyConnectionCallback: ((WifiData?) -> Unit)? = null
            internal var mWifiStateChangedListener: ((Int) -> Unit)? = null
            internal var mNetworkStateChangedListener: ((NetworkInfo.DetailedState) -> Unit)? = null
            internal var mErrorAuthenticating: ((String) -> Unit)? = null
            internal var mContext: Context? = context
    
            /**
             * 设置WiFi扫描回调
             */
            fun setScanCallback(callback: ((List<WifiData>?) -> Unit)): Build {
                mScanCallback = callback
                return this
            }
    
            /**
             * 设置当前正在连接的WiFi回调
             */
            fun setAlreadyConnectionCallback(callback: (WifiData?) -> Unit): Build {
                mAlreadyConnectionCallback = callback
                return this
            }
    
            /**
             * 设置连接时密码错误回调
             */
            fun setErrorAuthenticating(callback: (String) -> Unit): Build {
                mErrorAuthenticating = callback
                return this
            }
    
            /**
             * WiFi开关状态监听
             *  WifiManager.WIFI_STATE_DISABLED  //WiFi已关闭
             *  WifiManager.WIFI_STATE_DISABLING //WiFi关闭中
             *  WifiManager.WIFI_STATE_ENABLED //WiFi已开启
             *  WifiManager.WIFI_STATE_ENABLING //WiFi开启中
             *  WifiManager.WIFI_STATE_UNKNOWN //WiFi状态未知
             */
            fun setWifiStateChangedListener(wifiStateChangedListener: (Int) -> Unit): Build {
                mWifiStateChangedListener = wifiStateChangedListener
                return this
            }
    
            /**
             * 当前连接WiFi网络状态的变化,这个监听是指当前已经连接WiFi的断开与重连的状态
             * NetworkInfo.DetailedState.CONNECTED              //已经连接
             * NetworkInfo.DetailedState.DISCONNECTED           //已经断开
             * NetworkInfo.DetailedState.IDLE                   //空闲中
             * NetworkInfo.DetailedState.AUTHENTICATING         //认证中
             * NetworkInfo.DetailedState.BLOCKED                //认证失败
             * NetworkInfo.DetailedState.CAPTIVE_PORTAL_CHECK //连接检查
             */
            fun setNetworkStateChangedListener(networkStateChangedListener: (NetworkInfo.DetailedState) -> Unit): Build {
                mNetworkStateChangedListener = networkStateChangedListener
                return this
            }
    
            fun build(): WifiHelp {
                val wifiHelp = WifiHelp()
                wifiHelp.init(this)
                return wifiHelp
            }
        }
    
        /**
         * WiFi监听
         */
        protected inner class WiFiChangeReceiver : BroadcastReceiver() {
    
            override fun onReceive(context: Context, intent: Intent) {
                //WiFi开关状态
                if (intent.action == WifiManager.WIFI_STATE_CHANGED_ACTION) {
                    val switchState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0)//得到WiFi开关状态值
                    mBuild?.mWifiStateChangedListener?.invoke(switchState)
                }
                //当前连接WiFi状态的变化,这个监听是指当前已经连接WiFi的断开与重连的状态
                if (intent.action == WifiManager.NETWORK_STATE_CHANGED_ACTION) {
                    //得到信息包
                    val parcelableExtra: Parcelable? = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO)
                    val networkInfo: NetworkInfo = parcelableExtra as NetworkInfo
                    mBuild?.mNetworkStateChangedListener?.invoke(networkInfo.detailedState)
                    //在每次连接成功or连接失败后更新一次数据
                    if (networkInfo.detailedState == NetworkInfo.DetailedState.CONNECTED
                        || networkInfo.detailedState == NetworkInfo.DetailedState.DISCONNECTED
                    ) {
                        refreshWifiData()
                    }
                }
                //信号变化
                if (intent.action == WifiManager.RSSI_CHANGED_ACTION) {
                    refreshConnectWifiData()
                }
                //搜索完成
                if (intent.action == WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) {
                    refreshWifiData()
                }
                //请求变化
                if (intent.action == WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) {
                    val linkWifiResult = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, 0)
                    if (linkWifiResult == WifiManager.ERROR_AUTHENTICATING) {
                        mBuild?.mErrorAuthenticating?.invoke("密码错误")
                    }
                }
            }
        }
    
    }
    
    /**
     * wifi数据
     */
    data class WifiData(
        val netId: Int,                 //WiFi网络id,只有已经保存密码的WiFi才有,用于重连网络
        val ssid: String,               //WiFi名称
        val rssi: Int,                  //WiFi信号强度 0到4  0信号最差 4信号最好
        val bssid: String?,             //WiFi地址
        val ipAddress: String?,         //ip地址(连接WiFi后才会有)
        val capabilities: WiFiPwdType,  //WiFi加密方式
        val isSavePwd: Boolean          //是否保存密码
    )
    
    enum class WiFiPwdType {
        //加密方式
        /**
         *  ESS = 开放网络,不加密,无需密码
         */
        ESS,
    
        /**
         * WEP = 旧的加密方式,不推荐使用,仅需密码
         */
        WEP,
    
        /**
         * WAP  = 最常见的加密方式,仅需密码
         */
        WAP,
    
        /**
         * WPA2-EAP  = 企业加密方式,ID+密码验证
         */
        WPA2_EAP,
    }
  • 相关阅读:
    IE6 select穿透问题(div 定位无法遮盖select)!
    NYOJ 35 表达式求值
    网络运营商名称显示&amp;SIM名称显示
    Android开发四大组件之Service(具体解释篇)
    RabbitMQ之消息确认机制(事务+Confirm)
    rabbitmq的发布确认和事务
    java finally return
    用 consul + consul-template + registrator + nginx 打造真正可动态扩展的服务架构
    consul vs etcd3
    LinkedIn实时低延迟数据抓取系统Databus开源
  • 原文地址:https://www.cnblogs.com/guanxinjing/p/16307287.html
Copyright © 2020-2023  润新知