Android应用内连接WIFI(适配Android10)

2023.9.25更新:
这个文章只能提供作为参考,前段时间调研了一下android应用内连接wifi的情况,
目前有三种方式
1.直接添加wifi配置,直接进行连接(android 10及以后禁止添加wifi配置 想通过此方法在高版本android实现连接 只能将项目target sdk设置为小于等于28 但这样做又会导致谷歌市场拒绝上架 国内应用市场似乎不受影响)
2.通过suggest进行连接(系统看网络情况决定是否要连接)
3.通过平p2p方式连接 (无法上网 用于数据传输场景 不能用)

代码只有参考作用 推荐方式1 但要将target sdk设置为小于等于28 如果你想上架谷歌市场,目前没有一个比较好的方案实现应用内连接


本文主要参考了官方的开发文档

适用于互联网连接的 WLAN 建议 API
适用于对等连接的 WLAN 网络请求 API

一.Android 10版本和10以下关于wifi连接的区别

1.Android10不允许应用添加系统的网络配置,但是官方提供了一个新的方案来让应用进行连接wifi

这个新的方案就是“向系统提建议”,就是我在应用中告诉系统,这里有一个wifi可以连接,它的名称是什么什么,密码是什么什么。
系统收到这个建议后,会根据不同情况来决定是否要接受这个应用的建议。如果接受了就会发出广播通知你,提出建议后只需要准备好一个广播接收器就好了
但是实际测试下来感觉系统很高傲呀,在本身已经连接其他wifi的情况下根本就不会理你,这该怎么办呢

我在文档上还发现了一个P2P的连接:适用于对等连接的 WLAN 网络请求 API
尝试用这个来进行wifi连接,居然可行,那就暂时先这样了

2.Android10不允许应用打开/关闭wifi开关

这个没办法,Android 10只能打开设置中的wifi界面让用户自己打开了

二. 一个wifi连接工具类

使用之前一定要请求位置权限,因为要获取wifi列表,而根据wifi列表是可以计算出位置信息的

 

wifi连接工具类:


@SuppressLint("MissingPermission")
class WifiTools {
    //位置权限!!
    companion object {
        val instance: WifiTools by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { WifiTools() }
    }

    private val TAG = "wifi操作"//网络名称
    private val context = App.context
    private val wifiManager =
        context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager

    @SuppressLint("MissingPermission")
    fun connectWifi(ssid: String, password: String) {
        openWifi()
        val resssss=wifiManager.scanResults
        resssss.size
        val scanResult = wifiManager.scanResults.singleOrNull { it.SSID == ssid }
        if (scanResult == null) {
            Toast.makeText(context, context.getString(R.string.search_wifi_fail), Toast.LENGTH_SHORT).show()
            return
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                connectByP2P(ssid, password)
                return
            }
            var isSuccess = false
            //如果找到了wifi了,从配置表中搜索该wifi的配置config,也就是以前有没有连接过
            //注意configuredNetworks中的ssid,系统源码中加上了双引号,这里比对的时候要去掉
            val config =
                wifiManager.configuredNetworks.singleOrNull { it.SSID.replace("\"", "") == ssid }
            isSuccess = if (config != null) {
                //如果找到了,那么直接连接,不要调用wifiManager.addNetwork  这个方法会更改config的!
                wifiManager.enableNetwork(config.networkId, true)
            } else {
                // 没找到的话,就创建一个新的配置,然后正常的addNetWork、enableNetwork即可
                val padWifiNetwork =
                    createWifiConfig(
                        scanResult.SSID,
                        password,
                        getCipherType(scanResult.capabilities)
                    )
                val netId = wifiManager.addNetwork(padWifiNetwork)
                wifiManager.enableNetwork(netId, true)
            }
            if (isSuccess) {
                Toast.makeText(context, context.getString(R.string.connect_success), Toast.LENGTH_SHORT).show()

            } else {
                Toast.makeText(context, context.getString(R.string.connect_fail), Toast.LENGTH_SHORT).show()

            }

        }
    }

    private fun openWifi() {
        if (!wifiManager.isWifiEnabled) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                //请用户手动打开wifi
                Toast.makeText(context, context.getString(R.string.open_wifi_hint), Toast.LENGTH_SHORT).show()
                //这里可以使用event bus代替 在activity接收到后 打开wifi的设置界面
                DarkmagicMessageManager.send(MessageAction.OPENWIFISETTING)
            } else {
                wifiManager.isWifiEnabled = true
            }
        }
    }

    private fun startScantWifi() {
        val wifiScanReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                Log.d(TAG, "Wifi扫描完成")
                val results = wifiManager.scanResults//结果

            }
        }
        val intentFilter = IntentFilter()
        intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
        context.registerReceiver(wifiScanReceiver, intentFilter)
        wifiManager.startScan()
    }

    //Android8以下 通过Config连接Wifi
    private fun connectByConfig() {

    }

    //Android10以上 通过P2P连接Wifi
    @RequiresApi(Build.VERSION_CODES.Q)
    private fun connectByP2P(ssid: String, password: String) {
        val specifier = WifiNetworkSpecifier.Builder()
            .setSsid(ssid)
            .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?) {
                // do success processing here..
                Toast.makeText(context, context.getString(R.string.connect_success), Toast.LENGTH_SHORT).show()
                
            }

            override fun onUnavailable() {
                // do failure processing here..
                Toast.makeText(context, context.getString(R.string.connect_fail), Toast.LENGTH_SHORT).show()
            }
        }

        connectivityManager.requestNetwork(request, networkCallback)

    }

    //Android10以上,通过suggestion连接WIFI
    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)
        Log.d(TAG, status.toString())
        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);

    }


    private 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 = wifiManager.configuredNetworks.singleOrNull { it.SSID == "\"$ssid\"" }
        if (tempConfig != null) {
            //则清除旧有配置  不是自己创建的network 这里其实是删不掉的
            wifiManager.removeNetwork(tempConfig.networkId)
            wifiManager.saveConfiguration()
        }

        //不需要密码的场景
        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加密的场景,自己测试时,发现热点以WPA2建立时,同样可以用这种配置连接
        } 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
    }

    private 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
}

注释也给的很清晰,简单说一下,android10采用p2p连接wifi,android10以下采用原本的添加wifi配置的方式进行连接。

用法:

 WifiTools.instance.connectWifi(ssid, password)

三. 后续一些想说的话

以后遇到问题多多研究一下官方的开发者文档,真的非常详细且规范。很多问题国内的各家CSDN,博客园,甚至,内容都太陈旧过时了。只有官方我文档是和系统同步进行更新的。

在实际测试中,发现竞品居然可以在android10上直接连接wifi,这是我没有想到的。也就是说这个P2P连接wifi的方式并非最优解,看来学习之路漫漫呀

对了关于打开系统设置的wifi界面代码在这里:

startActivity(Intent(android.provider.Settings.ACTION_WIFI_SETTINGS))

就一句话搞定

你可能感兴趣的:(Android应用内连接WIFI(适配Android10))