Android Wifi扫描和连接监听(2022/08/16)

脑图

wifi_xmind.png

1.WifiManager

基础功能

interface IWifiManager {
    // wifi是否打开
    fun isWifiEnabled(): Boolean

    // 操作wifi开关
    fun switchWifi(isOpen: Boolean)

    // 开始扫描附近wifi
    fun startScan()

    // 扫描后,获取扫描wifi列表
    fun getScanResults(): MutableList

    // 主动连接wifi,password为空则为无密码
    fun connectWifi(wifiBean: WifiBean, password: String?): Boolean

    // 忘记wifi
    fun forgetWifi(wifiBean: WifiBean): Boolean
}

实现流程

1.权限检测

  /**
    @description 权限相关
     */
    private val wifiPermission = activity.multiplePermissionsForResult()
    private val permissionList = arrayOf(
        Manifest.permission.CHANGE_WIFI_STATE,
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION,
        Manifest.permission.WRITE_SETTINGS //用于sdk29调用系统wifi设置权限用
    )

activity.multiplePermissionsForResult():

使用了registerForActivityResult(resultContract: ActivityResultContract,callback: ActivityResultCallback) 这是新的权限申请方式,具体可看官方文档。方法在LifecycleOwner中,调用所需的俩个参数,resultContract是请求结果协议类,用于包裹输入值I和输出值O,其中输入值例如权限传递就是String(Manifest.permission.CHANGE_WIFI_STATE),输出值是Boolean,返回权限授权结果。第二个参数值callback,是onActivityResult的结果,用于最终结果回调。

Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION

这三个权限是为了兼容8.0、9.0中所需申请的wifi权限,用于wifi状态的监听和wifi扫描

Manifest.permission.WRITE_SETTINGS //用于sdk29调用系统wifi设置权限用

 private fun checkPermission(block: () -> Unit) {
        if (permissionList.hasPermission()) {
            block.invoke()
        } else {
            wifiPermission.launch(permissionList) {
                // 检测权限完整
                var hasPermission = false
                for (entry in it) {
                    if (!entry.value) {
                        hasPermission = false
                        break
                    } else {
                        hasPermission = true
                    }
                }
                if (hasPermission) block.invoke()
            }
        }
    }

2.基础功能的具体实现

// 系统WifiManager的获取
    val wifiManager by lazy {
        activity.getSystemService(Context.WIFI_SERVICE) as WifiManager
    }

    override fun isWifiEnabled(): Boolean {
        return wifiManager.isWifiEnabled
    }

    override fun switchWifi(isOpen: Boolean) {
//        新版本如果是android29的,只能通过上面这种凡是操作wifi
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//            val panelIntent = Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY)
//            activity.startActivity(panelIntent)
//        } else {
//            wifiManager.isWifiEnabled = isOpen
//        }
        wifiManager.isWifiEnabled = isOpen
    }

    override fun startScan() {
        checkPermission {
            wifiManager.startScan()
        }
    }

    /**
    @description 获取系统扫描的wifi列表信息
     */
    override fun getScanResults(): MutableList {
        //过滤空的ssid并去重
        val resultMap =
            wifiManager.scanResults?.filter { !it.SSID.isNullOrBlank() }?.groupBy { it.SSID }
        val scanResults = mutableListOf()
        if (resultMap.isNullOrEmpty()) {
            return scanResults
        }

        for ((_, value) in resultMap) {
            // 取首个值
            value.firstOrNull()?.let {
                scanResults.add(it)
            }
        }
        return scanResults
    }

    /**
    @description 连接有密码的wifi,只支持版本低于29
    @return 是否连接成功
     */
    override fun connectWifi(wifiBean: WifiBean, password: String?): Boolean {
        if (!wifiManager.isWifiEnabled) {
            return false
        }
        // 无密码连接
        if (password == null) {
            val scanResult = wifiManager.scanResults.filter { it.SSID == wifiBean.ssid }
            scanResult.forEach {
                if (connectScanResult(it)) {
                    return true
                }
            }
            return false
        } else {
            val padWifiNetwork =
                createWifiConfig(
                    wifiBean.ssid ?: "",
                    password,
                    WifiBean.getCapabilityTag(wifiBean.capabilities)
                )
            return wifiManager.enableNetwork(wifiManager.addNetwork(padWifiNetwork), true)
        }
    }

    override fun forgetWifi(wifiBean: WifiBean): Boolean {
        val configurations = wifiManager.configuredNetworks
        for (configuration in configurations) {
            val replace = configuration.SSID.replace("\"", "")
            if (replace == wifiBean.ssid) {
                var removeResult = wifiManager.removeNetwork(configuration.networkId)
                removeResult = removeResult and wifiManager.saveConfiguration()
                return removeResult
            }
        }
        return false
    }

主要对IWifiMananger的接口方法进行实现,也是定义了对外调用最近本的几个方法

3.其他功能方法

/**
@description 主动连接扫描到的wifi
@param scanResult 系统wifi扫描返回的信息类
@return 是否连接成功
*/
private fun connectScanResult(scanResult: ScanResult?): Boolean {
    return if (scanResult != null) {
        // 1.查找曾经的wifi配置,已连上系统会存储下来
        val config = getWifiConfig(scanResult.SSID)
        if (config != null && config.status != WifiConfiguration.Status.DISABLED) {
            if (BuildConfig.DEBUG) {
                logSettingD(TAG, "找到了历史wifi:{scanResult.SSID}")
            }
            // 1.1找到以后调用连接
            wifiManager.enableNetwork(config.networkId, true)
        } else {
            // 2.没有wifi配置,判断当前模板wifi加密方式
            val capabilityTag = WifiBean.getCapabilityTag(scanResult.capabilities)
            if (capabilityTag == WifiBean.CapabilityTag.NONE) {
                // 2.1无加密,直接连接
                val padWifiNetwork =
                    createWifiConfig(
                        scanResult.SSID,
                        capabilityTag = WifiBean.getCapabilityTag(scanResult.capabilities)
                    )
                val netId = wifiManager.addNetwork(padWifiNetwork)
                if (BuildConfig.DEBUG) {
                    logSettingD(TAG, "不需要密码连接wifi:{scanResult.SSID}")
                }
                wifiManager.enableNetwork(netId, true)
                wifiManager.reconnect()
            } else {
                // 2.2有加密,并且曾经也没有wifi配置,自然不需要主动连接
                if (BuildConfig.DEBUG) {
                    logSettingD(TAG, "需要密码连接wifi:${scanResult.SSID}")
                }
                false
            }
        }
    } else {
        if (BuildConfig.DEBUG) {
            logSettingD(TAG, "connectWifi 没有找到")
        }
        false
    }
}

/**
@description 将"ssid"替换成ssid
*/
private fun getWifiConfig(ssid: String?): WifiConfiguration? {
    return wifiManager.configuredNetworks.firstOrNull {
        it.SSID.replace("\"", "") == ssid
    }
}

/**
@description 根据ssid、密码、加密方式,生产wifi配置信息
*/
private fun createWifiConfig(
    ssid: String,
    password: String = "",
    capabilityTag: WifiBean.CapabilityTag
): WifiConfiguration {
    // 1.初始化WifiConfiguration
    val config = WifiConfiguration()
    config.allowedAuthAlgorithms.clear()
    config.allowedGroupCiphers.clear()
    config.allowedKeyManagement.clear()
    config.allowedPairwiseCiphers.clear()
    config.allowedProtocols.clear()

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

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

    // 3.根据加密方式生成不同配置
    when (capabilityTag) {
        WifiBean.CapabilityTag.NONE -> {
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
            //以WEP加密的场景
        }
        WifiBean.CapabilityTag.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加密的场景
        }
        WifiBean.CapabilityTag.PSK -> {
            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
        }
        else -> {

        }
    }
    return config
}

/**
@description 获取wifi列表信息,WifiBean是期望的数据类型
 */
fun getWifiBeanList(): List {
    //按照强度排序
    val wifiList = getScanResults()
        .sortedByDescending { it.level }
        .map { scanResult ->
            WifiBean.transform(scanResult)
        }

    val connectionInfoSsid = wifiManager.connectionInfo?.ssid?.replace("\"", "")
    logSettingD(WifiProxy.TAG, "getWifiBeanList: connectionInfoSsid = $connectionInfoSsid")

    return changeWifiListBySsid(connectionInfoSsid, WifiBean.ConnectStatus.CONNECTED, wifiList)
}

/**
@description 修改匹配后的wifiBean列表
@param ssid 要匹配的ssid
@param targetStatus 目标状态
@param list 要修改的wifiList数据
 */
fun changeWifiListBySsid(
    ssid: String?,
    targetStatus: WifiBean.ConnectStatus,
    list: List
): MutableList {
    val temps: MutableList = mutableListOf()
    temps.addAll(list)
    // 为空直接返回原有数据列表
    if (ssid == null) return temps

    for (i in list.indices) {
        val temp = list[i]
        if (ssid == temp.ssid) {
            temp.connectStatus = targetStatus
            temps.remove(temp)
            temps.add(0, temp)
        } else {
            temp.connectStatus = WifiBean.ConnectStatus.DISCONNECT
        }
    }
    return temps
}

2.WifiScanReceiver

/**
@description Wifi连接状态接收器
@param switchListener wifi开关状态监听
@param scanSucListener wifi扫描结果是否成功
@param connectListener wifi请求连接的状态监听
 */
class WifiScanReceiver(
    private val wifiManager: WifiManager,
    private val switchListener: (Boolean) -> Unit,
    private val scanSucListener: (Boolean) -> Unit,
    private val connectListener: (String, Boolean) -> Unit,
) : BroadcastReceiver() {

    companion object {
        const val TAG = "WifiScanReceiver"
    }

    fun register(activity: AppCompatActivity) {
        activity.registerReceiver(this, IntentFilter().apply {
            addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
            addAction(WifiManager.WIFI_STATE_CHANGED_ACTION)
            addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)
            addAction(WifiManager.RSSI_CHANGED_ACTION)
        })
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        logSettingD(TAG, "onReceive: action = ${intent?.action}")
        intent?.run {
            when (action) {
                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION -> {
                    scanSucListener.invoke(isScanSuc(intent))
                }
                WifiManager.WIFI_STATE_CHANGED_ACTION -> {
                    handleSwitchChange(intent)
                }
                WifiManager.SUPPLICANT_STATE_CHANGED_ACTION -> {
                    handleSupplicantState(intent)
                }
                WifiManager.RSSI_CHANGED_ACTION ->{

                }
            }
        }
    }

    /**
    @description 处理wifi连接请求的状态变化
     */
    private fun handleSupplicantState(intent: Intent) {
        // 密码错误
        val error = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1)
        if (error == WifiManager.ERROR_AUTHENTICATING) {
            wifiManager.connectionInfo?.ssid?.replace("\"", "")?.let {
                logSettingD(TAG, "获取连接状态:密码错误")
                connectListener.invoke(it, true)
            }
            return
        }
        // 获取连接状态
        val supplicantState: SupplicantState? =
            intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)
        when (supplicantState) {
            // 成功
            SupplicantState.COMPLETED -> {
                logSettingD(TAG, "获取连接状态:成功")
                wifiManager.connectionInfo?.ssid?.replace("\"", "")?.let {
                    connectListener.invoke(it, false)
                }
            }
            // 不活跃的
            SupplicantState.INACTIVE -> {
                logSettingD(TAG, "获取连接状态:不活跃的")
            }
            // 接口禁用
            SupplicantState.INTERFACE_DISABLED -> {
                logSettingD(TAG, "获取连接状态:接口禁用")
            }
            SupplicantState.DISCONNECTED -> {
                logSettingD(TAG, "获取连接状态:断开连接")
            }
            SupplicantState.SCANNING -> {
                logSettingD(TAG, "获取连接状态:正在扫描")
            }
            SupplicantState.AUTHENTICATING -> {
                logSettingD(TAG, "获取连接状态:正在验证")
            }
            SupplicantState.ASSOCIATING -> {
                logSettingD(TAG, "获取连接状态:正在关联")
            }
            SupplicantState.ASSOCIATED -> {
                logSettingD(TAG, "获取连接状态:已经关联")
            }
            SupplicantState.FOUR_WAY_HANDSHAKE -> {
                logSettingD(TAG, "获取连接状态:四次握手")
            }
            SupplicantState.GROUP_HANDSHAKE -> {
                logSettingD(TAG, "获取连接状态:组握手")
            }
            SupplicantState.DORMANT -> {
                logSettingD(TAG, "获取连接状态:休眠")
            }
            SupplicantState.UNINITIALIZED -> {
                logSettingD(TAG, "获取连接状态:未初始化")
            }
            SupplicantState.INVALID -> {
                logSettingD(TAG, "获取连接状态:无效的")
            }
            else -> {
                logSettingD(TAG, "wifi连接结果通知")
            }
        }
    }

    /**
    @description 处理wifi扫描结果是否成功
     */
    private fun isScanSuc(intent: Intent): Boolean {
        if (wifiManager.isWifiEnabled) {
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)
            } else {
                true
            }
        }
        return false
    }

    /**
    @description 处理wifi开关状态变化
     */
    private fun handleSwitchChange(intent: Intent) {
        val state =
            intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)
        logSettingD(TAG, "handleStateChange: state = ${getWifiStateStr(state)}")
        if (state == WifiManager.WIFI_STATE_ENABLED) {
            switchListener.invoke(true)
        } else if (state == WifiManager.WIFI_STATE_DISABLING || state == WifiManager.WIFI_STATE_DISABLED) {
            switchListener.invoke(false)
        }
    }

    private fun getWifiStateStr(state: Int): String {
        return when (state) {
            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未知状态"
            else -> "Wifi未知状态"
        }
    }
}

主要用于配合wifiManager,获取到三种监听

1.SCAN_RESULTS_AVAILABLE_ACTION:监听扫描结果,扫描成功或失败会触发

2.WIFI_STATE_CHANGED_ACTION:处理wifi开关状态变化时,进行回调处理

3.SUPPLICANT_STATE_CHANGED_ACTION处理wifi连接请求的状态变化

sdk29以后也出现了其他新的api监听回调,wifiManager.registerXXX()就能监听对应状态,但由于不兼容旧设备,因此全部wifi状态都用广播方式获取,广播获取频率会受到具体限制,具体情况详见官方文档

3.ConnectManager

/**
@author: Zed.Qiu
@date: 2022/8/15
@description: 用于管理网络状态发生变化的回调,以及处理信号变化回调
 */
class TyphurConnectManager(private var context: Context = TyphurCoreConst.app) {
    companion object {
        const val TAG = "TyphurConnectManager"

        @Volatile
        var instance: TyphurConnectManager? = null
        fun getInstance(context: Context = TyphurCoreConst.app): TyphurConnectManager {
            if (instance == null) {
                synchronized(TyphurConnectManager::class.java) {
                    if (instance == null) {
                        instance = TyphurConnectManager(context)
                    }
                }
            }
            instance?.context = context
            return instance!!
        }

        /**
        @description 是否有网络连接
         */
        fun isNetworkAvailable(context: Context): Boolean {
            val connectivityManager =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // 当前是否有网络
                val nw = connectivityManager.activeNetwork ?: return false
                val actNw = connectivityManager.getNetworkCapabilities(nw) ?: return false
                return when {
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
                    // 蜂窝网
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
                    // 以太网
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
                    // vpn
                    actNw.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> true
                    else -> false
                }
            } else {
                return connectivityManager.activeNetworkInfo?.isConnected ?: false
            }
        }
    }


    private var isRegister = false

    /**
    @description 网络状态变化回调管理列表
    */
    private val networkCallbackList = mutableListOf()
    /**
    @description 网络连接管理器
     */
    private val connectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    private val networkCallback = object :
        ConnectivityManager.NetworkCallback() {
        override fun onUnavailable() {
            super.onUnavailable()
            logSettingD(TAG, "NetworkCallback onUnavailable! 网络不可访问")
            networkCallbackList.forEach { callback ->
                callback.onUnavailable()
            }
        }

        override fun onAvailable(network: Network) {
            super.onAvailable(network)
            // 网络可访问时绑定
            val bindResult = connectivityManager.bindProcessToNetwork(network)
            logSettingD(TAG, "NetworkCallback onAvailable! 网络已连接 bindResult = $bindResult")
            networkCallbackList.forEach { callback ->
                callback.onAvailable(network)
            }
        }

        override fun onLosing(network: Network, maxMsToLive: Int) {
            super.onLosing(network, maxMsToLive)
            logSettingD(TAG, "NetworkCallback onLosing! 网络断开连接中")
            networkCallbackList.forEach { callback ->
                callback.onLosing(network, maxMsToLive)
            }
        }

        override fun onLost(network: Network) {
            super.onLost(network)
            logSettingD(TAG, "NetworkCallback onLost! 网络已断开连接")
            networkCallbackList.forEach { callback ->
                callback.onLost(network)
            }
        }

        override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities
        ) {
            super.onCapabilitiesChanged(network, networkCapabilities)
            logSettingD(TAG, "NetworkCallback onCapabilitiesChanged! 信号强度发生变化")
            logSettingD(TAG, "networkCapabilities = $networkCapabilities")
            networkCallbackList.forEach { callback ->
                callback.onCapabilitiesChanged(network, networkCapabilities)
            }
        }
    }
    init {
        register()
    }

    fun register(builder :NetworkRequest.Builder? =null) {
        if (isRegister) return
        if (builder == null){
            registerNetworkCallback()
        }else{
            connectivityManager.registerNetworkCallback(builder.build(), networkCallback)
        }
        isRegister = true
    }

    fun unRegister() {
        connectivityManager.bindProcessToNetwork(null)
        connectivityManager.unregisterNetworkCallback(networkCallback)
        networkCallbackList.clear()
        isRegister = false
    }

    /**
    @description 添加监听回调
    */
    fun addCallback(callback: ConnectivityManager.NetworkCallback?) {
        callback?.run {
            if (!networkCallbackList.contains(this)) {
                networkCallbackList.add(this)
            }
        }
    }

    /**
    @description 移除监听回调
     */
    fun removeCallback(callback: ConnectivityManager.NetworkCallback?) {
        networkCallbackList.remove(callback)
    }

    /**
    @description 注册wifi网络状态回调
     */
    private fun registerNetworkCallback() {
        val builder = NetworkRequest.Builder().apply {
            addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
            addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
            addTransportType(NetworkCapabilities.TRANSPORT_VPN)
            addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
        }
        connectivityManager.registerNetworkCallback(builder.build(), networkCallback)
    }
}

主要功能:

1.持有系统ConnectivityManager,并注册了相关网络变化回调

2.isNetworkAvailable(),可判断当前网络是否可达

3.生命周期跟随Application,只需注册一次,可在任一地方进行对网络状态的监听

调用方式如下:

// 获取TyphurConnectManager对象
private val typhurConnectManager by lazy { TyphurConnectManager.getInstance() }

// 添加网络回调, isConnect代表是否连接, level代表信号强度
typhurConnectManager.addCallback(TyphurConnectCallback { isConnect, level ->
    refreshByNetworkChange(isConnect, level)
}})

4.TyphurConnectCallback


/**
@author: Zed.Qiu
@date: 2022/8/15
@description: 默认网络回调
@param listener 参数1 是否联网 参数2 wifi信号等级
 */
class TyphurConnectCallback(var listener: Function2) : ConnectivityManager.NetworkCallback() {
    override fun onAvailable(network: Network) {
        super.onAvailable(network)
        onNetworkChange()
    }

    override fun onLosing(network: Network, maxMsToLive: Int) {
        super.onLosing(network, maxMsToLive)
        onNetworkChange()
    }

    override fun onLost(network: Network) {
        super.onLost(network)
        onNetworkChange()
    }

    /**
    @description 网络发生变化
    */
    private fun onNetworkChange() {
        val networkAvailable =
            TyphurConnectManager.isNetworkAvailable(TyphurCoreConst.app)
        TyphurCoreConst.mainScope.launchOnUi {
            listener.invoke(networkAvailable, WifiBean.LEVEL.LEVEL_UNKNOW)
        }
    }

    /**
    @description desc
    @param methodParameters
    @return methodReturnType
    */
    override fun onCapabilitiesChanged(
        network: Network,
        networkCapabilities: NetworkCapabilities
    ) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        TyphurCoreConst.mainScope.launchOnUi {
            val networkAvailable =
                TyphurConnectManager.isNetworkAvailable(TyphurCoreConst.app)
            val level = WifiBean.calculateLevel(networkCapabilities.signalStrength)
            listener.invoke(networkAvailable, level)
        }
    }
}

只是对系统的网络回调做了一层简单的封装,暴露期望的回调监听提供给其他人

5.WifiBean

1.属性介绍

属性名称 类型 作用
wifiName String wifi名称
levelValue Int 原始的信号值
ssid String wifi唯一标识
bssid String 无线网址的mac地址
connectStatus ConnectStatus ConnectStatus.DISCONNECT默认未不连接

2.方法介绍

方法名 作用
fun isCapabilityEAP(capabilities: String?): Boolean 是否为企业加密wifi eap
fun getCapabilityTag(capabilities: String?): CapabilityTag 获取加密类型标识
fun isCapabilityNone(capabilities: String?): Boolean 是否为不加密wifi
fun getCapabilityStr(capability: CapabilityTag): String 获取加密方式字符
fun calculateLevel(levelValue: Int): LEVEL 计算信号等级
fun transform(scanResult: ScanResult): WifiBean ScanResult:扫描结果类转WifiBean

3.具体实现

/**
 * @author zed
 */
@Keep
data class WifiBean(
    var wifiName: String? = null,
    var levelValue: Int = 0,
    var ssid: String? = null,
    var bssid: String? = null,
    var connectStatus: ConnectStatus = ConnectStatus.DISCONNECT,
    /**
     * 加密方式
     */
    var capabilities: String? = null
) {
    companion object {

        const val CAPABILITY_NONE_STR = "NONE"
        const val CAPABILITY_WEP_STR = "WEP"
        const val CAPABILITY_PSK_STR = "PSK"
        const val CAPABILITY_EAP_STR = "EAP"

        /**
        @description 获取加密类型标识
         */
        fun getCapabilityTag(capabilities: String?): CapabilityTag {
            capabilities?.run {
                return when {
                    this.contains(CAPABILITY_WEP_STR) -> CapabilityTag.WEP
                    this.contains(CAPABILITY_PSK_STR) -> CapabilityTag.PSK
                    this.contains(CAPABILITY_EAP_STR) -> CapabilityTag.EAP
                    else -> CapabilityTag.NONE
                }
            }
            return CapabilityTag.NONE
        }

        /**
        @description 是否为企业加密wifi eap
         */
        fun isCapabilityEAP(capabilities: String?): Boolean {
            return CapabilityTag.EAP == getCapabilityTag(capabilities)
        }

        /**
        @description 是否为不加密wifi
         */
        fun isCapabilityNone(capabilities: String?): Boolean {
            return CapabilityTag.NONE == getCapabilityTag(capabilities)
        }

        /**
        @description 获取加密方式字符
         */
        fun getCapabilityStr(capability: CapabilityTag): String {
            return when (capability) {
                CapabilityTag.WEP -> CAPABILITY_WEP_STR
                CapabilityTag.PSK -> CAPABILITY_PSK_STR
                CapabilityTag.EAP -> CAPABILITY_EAP_STR
                else -> CAPABILITY_NONE_STR
            }
        }

        /**
        @description 信号等级, level1 > level2 > level3
         */
        const val LEVEL1_ABS_VALUE = 50
        const val LEVEL2_ABS_VALUE = 75
        const val LEVEL3_ABS_VALUE = 90

        /**
        @description 计算信号等级
         */
        fun calculateLevel(levelValue: Int): LEVEL {
            val absValue = abs(levelValue)
            return when {
                absValue < LEVEL1_ABS_VALUE -> {
                    LEVEL.LEVEL1
                }
                absValue < LEVEL2_ABS_VALUE -> {
                    LEVEL.LEVEL2
                }
                absValue < LEVEL3_ABS_VALUE -> {
                    LEVEL.LEVEL3
                }
                absValue >= LEVEL3_ABS_VALUE -> {
                    LEVEL.LEVEL4
                }
                else -> {
                    LEVEL.LEVEL_UNKNOW
                }
            }
        }

        fun transform(scanResult: ScanResult): WifiBean {
            return WifiBean().apply {
                wifiName = scanResult.SSID?.replace("\"", "")
                ssid = scanResult.SSID
                bssid = scanResult.BSSID
                connectStatus = ConnectStatus.DISCONNECT
                capabilities = scanResult.capabilities
                levelValue = scanResult.level
            }
        }
    }

    /**
    @description 加密标识
    */
    enum class CapabilityTag {
        WEP, PSK, EAP, NONE
    }

    /**
    @description Wifi信号等级
     */
    enum class LEVEL {
        LEVEL_UNKNOW, LEVEL1, LEVEL2, LEVEL3, LEVEL4
    }

    /**
    @description 连接状态
     */
    enum class ConnectStatus {
        DISCONNECT, CONNECTED, CONNECTING
    }
}

6.WifiProxy

基本功能

/**
@author: Zed.Qiu
@date: 2022/8/2
@description: wifi代理类接口
 */
interface IWifiProxy {

    fun register()

    fun unRegister()

    /**
    @description 检测wifi是否连接
     */
    fun checkIsOpenWifi()

    /**
    @description 主动连接wifi
     */
    fun connectWifi(wifiBean: WifiBean, password: String?)
}

具体实现

/**
@author: Zed.Qiu
@date: 2022/8/2
@description: Wifi状态代理类
 */

class WifiProxy(
    val activity: AppCompatActivity,
    private val wifiBeanListListener: ((List) -> Unit),
    private val switchListener: Function1,
    private val connectListener: (String, Boolean) -> Unit
)  :IWifiProxy{
    companion object {
        const val TAG: String = "WifiProxy"
    }

    val typhurWifiManager by lazy {
        TyphurWifiManager.getInstance(activity)
    }

    private val typhurConnectManager by lazy {
        TyphurConnectManager.getInstance(activity)
    }

    private var receiver: WifiScanReceiver? = null

    private val networkCallback = object :
        ConnectivityManager.NetworkCallback() {
        override fun onUnavailable() {
            super.onUnavailable()
            activity.lifecycleScope.launchOnUi {
                wifiBeanListListener.invoke(typhurWifiManager.getWifiBeanList())
            }
        }

        override fun onAvailable(network: Network) {
            super.onAvailable(network)
            // 网络可访问时绑定

            activity.lifecycleScope.launchOnUi {
                wifiBeanListListener.invoke(typhurWifiManager.getWifiBeanList())
            }
        }

        override fun onLost(network: Network) {
            super.onLost(network)
            activity.lifecycleScope.launchOnUi {
                wifiBeanListListener.invoke(typhurWifiManager.getWifiBeanList())
            }
        }
    }

    override fun register() {
        registerWifiNetworkCallback()
        registerWifiScanReceiver()
    }

    override fun unRegister() {
        // 解绑当前网络
        activity.unregisterReceiver(receiver)
        typhurConnectManager.removeCallback(networkCallback)
    }

    /**
    @description 注册wifi网络状态回调
     */
    private fun registerWifiNetworkCallback() {
        typhurConnectManager.register()
        typhurConnectManager.addCallback(networkCallback)
    }

    /**
    @description 注册wifi扫描接收器
     */
    private fun registerWifiScanReceiver() {
        if (receiver == null) {
            receiver = WifiScanReceiver(
                typhurWifiManager.wifiManager, onWifiEnable(),
                onScanWifiList(), connectListener
            ).apply {
                register(activity)
            }
        }
    }

    /**
    @description 扫描返回最终的WifiBean列表
     */
    private fun onScanWifiList(): (Boolean) -> Unit = {
        if (it) {
            wifiBeanListListener.invoke(typhurWifiManager.getWifiBeanList())
        } else {
            wifiBeanListListener.invoke(listOf())
        }
    }

    /**
    @description wifi开关状态变化
     */
    private fun onWifiEnable(): (Boolean) -> Unit = {
        switchListener.invoke(it)
        if (it) {
            typhurWifiManager.startScan()
        } else {
            wifiBeanListListener.invoke(listOf())
        }
    }

    /**
     * 检查是否打开wifi
     */
    override fun checkIsOpenWifi() {
        val isWifiEnabled = typhurWifiManager.isWifiEnabled()
        switchListener.invoke(isWifiEnabled)
        if (isWifiEnabled) {
            typhurWifiManager.startScan()
        }
    }

    /**
    @description 连接wifi
    */
    override fun connectWifi(wifiBean: WifiBean, password: String?){
  /*      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            connectWifiSdk29(wifiBean, password)
        }else{
            typhurWifiManager.connectWifi(wifiBean, password)
        }*/
        typhurWifiManager.connectWifi(wifiBean, password)
    }

   /* *//**
    @description 版本大于等于29的新连接方法
    *//*
    private fun connectWifiSdk29(wifiBean: WifiBean, password: String?) {
        val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
            .apply {
                setSsid(wifiBean.ssid ?: "")
                if (password != null) {
                    setWpa2Passphrase(password)
                }
            }.build()

        val networkRequest = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .setNetworkSpecifier(wifiNetworkSpecifier)
            .build()
        connectivityManager.requestNetwork(networkRequest, networkCallback)
    }

    *//**
    @description 企业wifi EAP连接,暂不能实现
     *//*
    fun connectWifiEAP(wifiBean: WifiBean, identity: String, password: String) {
        val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
            .setSsid(wifiBean.ssid ?: "")
            .setWpa2EnterpriseConfig(WifiEnterpriseConfig().apply {
                eapMethod = WifiEnterpriseConfig.Eap.PEAP
                this.identity = identity
                this.password = password
                phase2Method = WifiEnterpriseConfig.Phase2.NONE
            })
            .build()

        val networkRequest = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .setNetworkSpecifier(wifiNetworkSpecifier)
            .build()
        connectivityManager.requestNetwork(networkRequest, networkCallback)
    }
*/
}

本质上,是持有俩个Manager,分别是TyphurConnectManager(管理网络状态变化)和TyphurWifiManager(管理wifi列表信息变化),另外对sdk版本29,做了一些差异化的网络连接处理,由于系统会强制弹窗wifi列表,并且不能连接上部分类型wifi,因此在这里注释调了,但旧方法还是可以用的,只是target sdk必须是28。android在sdk29开始就不允许应用层面去修改wifi相关配置,在sdk31也做了大量方法舍弃,在综合设备兼容后,还是考虑用旧版本实现。

compileSdk Integer.parseInt(project.findProperty("android.compile.sdk"))

defaultConfig {
    applicationId "com.typhur.module.demo"
    minSdk Integer.parseInt(project.findProperty("android.min.sdk"))
    targetSdk Integer.parseInt(project.findProperty("android.target.sdk"))
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

//gradle.properties文件
#android sdk info
android.compile.sdk=32
android.min.sdk=28
android.target.sdk=28

7.WifiLifecycleHelper

/**
@author: Zed.Qiu
@date: 2022/8/2
@description: Wifi生命周期管理器
 */
class WifiLifecycleHelper(
    private val activity: AppCompatActivity,
    private val wifiProxy: WifiProxy
) : LifecycleEventObserver {
    init {
        activity.lifecycle.addObserver(this)
    }

    private lateinit var typhurWifiManager: TyphurWifiManager

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        when (event) {
            Lifecycle.Event.ON_CREATE -> {
                lifeCycleCreate()
            }
            Lifecycle.Event.ON_START -> {

            }
            Lifecycle.Event.ON_RESUME -> {

            }
            Lifecycle.Event.ON_PAUSE -> {

            }
            Lifecycle.Event.ON_STOP -> {

            }
            Lifecycle.Event.ON_DESTROY -> {
                lifeCycleDestroy()
            }
            Lifecycle.Event.ON_ANY -> {

            }
        }
    }

    private fun lifeCycleCreate() {
        typhurWifiManager = TyphurWifiManager.getInstance(activity)
        wifiProxy.register()
        wifiProxy.checkIsOpenWifi()
    }

    private fun lifeCycleDestroy() {
        wifiProxy.unRegister()
    }

    fun isWifiEnabled(): Boolean {
        return typhurWifiManager.isWifiEnabled()
    }

    fun switchWifi(wifiEnabled : Boolean){
        typhurWifiManager.switchWifi(wifiEnabled)
    }

    fun forgetWifi(wifiBean: WifiBean){
        typhurWifiManager.forgetWifi(wifiBean)
    }

    fun changeWifiListBySsid(
        ssid: String?,
        targetStatus: WifiBean.ConnectStatus,
        list: List
    ): MutableList {
        return typhurWifiManager.changeWifiListBySsid(ssid, targetStatus, list)
    }

    fun connectWifi(wifiBean: WifiBean, password: String?){
        wifiProxy.connectWifi(wifiBean, password)
    }

    fun connectWifiEAP(wifiBean: WifiBean, identity: String, password: String){
//        wifiProxy.connectWifiEAP(wifiBean, identity, password)
    }
}

只是简单的生命周期辅助类,对所有方法同意封装了下

8.UI调用

wifiHelper = WifiLifecycleHelper(
    this@SettingWifiActivity, WifiProxy(this,
        { list ->
            Log.d("WifiProxy", "refreshList: ${list.toJsonString()}")
            adapter.refreshList(list)
        },
        {
            mBinding.btnTop.text = if (it) "关" else "开"
        }, { ssid, isPwError ->
            if (isPwError) {
                Toast.makeText(this, "${connectWifiBean?.ssid} 密码错误", Toast.LENGTH_SHORT)
                    .show()
            }
        })
)
// 主动连接wifi,兼容sdk版本
wifiHelper.connectWifi(wifiBean, password)  
// 当前是否有网络连接
val networkAvailable = WifiProxy.isNetworkAvailable(context)  
// 判断当前wifi是否开启
val wifiEnabled = wifiHelper.isWifiEnabled()  
// 切换开关
wifiHelper.switchWifi(!wifiEnabled)  
// 忘记wifi
wifiHelper.forgetWifi(wifiBean)  
// 修改wifi列表中某个wifi的连接状态
wifiHelper.changeWifiListBySsid(
 wifiBean.ssid, wifiBean.connectStatus, adapter.currentList)  

你可能感兴趣的:(Android Wifi扫描和连接监听(2022/08/16))