Target SDK 30的Android应用WiFi连接与跨应用网络异常求解
Android 目标SDK 30 WiFi连接适配问题
众所周知,Google已更新应用分发政策,要求新应用必须将目标SDK版本设置为Android 11(API level 30)及以上。我的应用在目标SDK版本低于30时运行正常,但将目标SDK升级到30后出现功能异常。
应用核心功能
- 适配Android 6到11全版本,展示当前可用WiFi列表
- 用户点击列表项后可连接对应WiFi,支持开放网络、加密网络等所有类型
- 连接WiFi后,若存在认证门户则跳转至应用内嵌WebView的认证页面
当前问题及相关实现代码
问题1:WiFi网络建议方案切换网络失败
目标SDK 30下使用官方推荐的WiFi网络建议方案连接WiFi时,若设备此前已连接其他WiFi,尝试切换连接新WiFi时会失败,无法成功切换到目标新WiFi,对应实现代码如下:
val suggestion = WifiNetworkSuggestion.Builder() .setSsid(SSID) // SSID of network .setWpa2Passphrase(wifiPassword) // password is network is not open //.setIsAppInteractionRequired(true) // Optional (Needs location permission) .build() val suggestionsList = listOf(suggestion) val wifiManager = applicationContext.getSystemService(WIFI_SERVICE) as WifiManager if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) { // do error handling here Log.e("NETWORK", "Error") } // Optional (Wait for post connection broadcast to one of your suggestions) val intentFilter = IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION); val broadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Log.e("NETWORK", "broadcastReceiver") if (!intent.action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) { return; } // do post connect processing here Log.e("NETWORK", "post connect") } }; registerReceiver(broadcastReceiver, intentFilter)
问题2:WifiNetworkSpecifier方案仅本应用可访问网络
此前目标SDK为29时,使用WifiNetworkSpecifier方案实现Android 10、11版本的WiFi连接功能运行正常,但如果目标SDK升级到30,使用该方案连接WiFi后仅当前应用可访问网络,应用外其他进程无法使用该WiFi网络,对应实现代码如下:
private fun android10andMoreVersionsWithoutOuterInternet( scanResult: ScanResult, wifiSSID: String, wifiPassword: String, capabilities: String ) { // Android 10 (API level 29) -- Android Q (Android 10) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val wifiManager = this.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder() .setSsid(wifiSSID) //.setSsidPattern(PatternMatcher(wifiSSID, PatternMatcher.PATTERN_PREFIX)) .setWpa2Passphrase(wifiPassword) .build() val networkRequest = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) //.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) //.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) //.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) .setNetworkSpecifier(wifiNetworkSpecifier) .build() val connectivityManager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val networkCallback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { Log.d("NETWORK", "Network available") super.onAvailable(network) // To make sure that requests don't go over mobile data connectivityManager.bindProcessToNetwork(network) //unregister network callback //connectivityManager.unregisterNetworkCallback(this) // connectivityManager.bindProcessToNetwork(null) gotoNextScreen(scanResult, wifiManager) } override fun onUnavailable() { Log.d("NETWORK", "Network unavailable") super.onUnavailable() } override fun onLosing(network: Network, maxMsToLive: Int) { Log.d("NETWORK", "onLosing") super.onLosing(network, maxMsToLive) } override fun onLost(network: Network) { Log.d("NETWORK", "onLost") super.onLost(network) //connectivityManager.bindProcessToNetwork(null) //connectivityManager.unregisterNetworkCallback(this) } } connectivityManager.requestNetwork(networkRequest, networkCallback) val builder = NetworkRequest.Builder() builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI) connectivityManager.registerNetworkCallback(builder.build(), networkCallback) //connectivityManager.registerNetworkCallback(networkRequest, networkCallback) // For listen } }
Android 9及以下版本实现(无适配问题)
Android 9及更低版本使用已废弃的WifiConfiguration相关旧API实现,功能完全正常,不受目标SDK版本影响,对应实现代码如下:
private fun android9AndPreviousVersion( scanResult: ScanResult, wifiSSID: String, wifiPassword: String, capabilities: String ) { val conf = WifiConfiguration() conf.SSID = "\"" + wifiSSID + "\"" // Please note the quotes. String should contain ssid in quotes conf.status = WifiConfiguration.Status.ENABLED conf.priority = 40 if (Common.checkWifiType(capabilities) == "WEP") { Log.e("NETWORK", "Configuring WEP") conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE) conf.allowedProtocols.set(WifiConfiguration.Protocol.RSN) conf.allowedProtocols.set(WifiConfiguration.Protocol.WPA) conf.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN) conf.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED) conf.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP) conf.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP) conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40) conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104) if (wifiPassword.matches(Regex("^[0-9a-fA-F]+$"))) { conf.wepKeys[0] = wifiPassword } else { conf.wepKeys[0] = "\"" + wifiPassword + "\"" } conf.wepTxKeyIndex = 0 } else if (Common.checkWifiType(capabilities) == "WPA") { Log.e("NETWORK", "Configuring WPA") conf.allowedProtocols.set(WifiConfiguration.Protocol.RSN) conf.allowedProtocols.set(WifiConfiguration.Protocol.WPA) conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK) conf.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP) conf.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP) conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40) conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104) conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP) conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP) conf.preSharedKey = "\"" + wifiPassword + "\"" } else { Log.e("NETWORK", "Configuring OPEN network") conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE) } val wifiManager = this.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager val networkId = wifiManager.addNetwork(conf) Log.e("NETWORK", "Add result $networkId") if (ActivityCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { return } val list = wifiManager.configuredNetworks for (i in list) { if (i.SSID != null && i.SSID == "\"" + wifiSSID + "\"") { Log.e("NETWORK", "WifiConfiguration SSID " + i.SSID) val isDisconnected = wifiManager.disconnect() Log.e("NETWORK", "isDisconnected : $isDisconnected") val isEnabled = wifiManager.enableNetwork(i.networkId, true) Log.e("NETWORK", "isEnabled : $isEnabled") val isReconnected = wifiManager.reconnect() Log.e("NETWORK", "isReconnected : $isReconnected") break } } //val connectionInfo: WifiInfo = wifiManager.getConnectionInfo() gotoNextScreen(scanResult, wifiManager) }
问题总结
目标SDK为30时,使用WifiNetworkSpecifier连接WiFi可成功,但仅当前应用可访问网络,其他应用无法使用该WiFi网络;使用WiFi网络建议方案连接WiFi时,无法从已连接的其他WiFi切换到新的目标WiFi,该问题在Android 10、11设备上均可复现。
需求
需要适配目标SDK 30的解决方案,覆盖Android 6到11全版本,连接WiFi后全设备所有应用均可正常访问该网络。
内容的提问来源于stack exchange,提问作者rockstar
相关产品推荐
相关产品推荐

