美文网首页AndroidAndroid
Android应用内连接WIFI(适配Android10)

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

作者: 番茄tomato | 来源:发表于2020-08-18 14:17 被阅读0次

    本文主要参考了官方的开发文档
    适用于互联网连接的 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列表是可以计算出位置信息的

     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    

    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)

        本文链接:https://www.haomeiwen.com/subject/wuahjktx.html