我有一个汽车伴侣应用程序,需要与wifi和移动数据网络进行通信。
我的车辆控制单元提供了一个无法访问互联网的wifi网络,该网络公开了我们可以从该应用调用的API服务。除此之外,我们还需要使用手机移动数据(3G / 4G)与可从互联网访问的另一个后端进行通信。
我立即注意到,某些android手机在没有互联网的情况下连接到wifi网络使用android设置菜单时,显示系统对话框,通知用户当前网络无法访问互联网。这里用户有两个选择:保持此wifi网络或断开连接并切换到另一个。
这里有一些例子:
Motorola moto G7 power - Android 9.0
经过简短的分析,我了解到,如果用户单击“否”选项,则系统会与当前的wifi网络断开连接,并且如果有其他网络可用,请连接到该网络。
如果用户单击“是”选项,我们可以有两种不同的行为:
然后我尝试了同样的事情,但是使用程序化连接
fun connectToWifi(ssid: String, key: String) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { connectPost10(ssid, key) } else { connectPre10(ssid, key) } } @RequiresApi(Build.VERSION_CODES.O) private fun connectPost10(ssid: String, wpa2Passphrase: String) { val specifier = WifiNetworkSpecifier.Builder() .setSsid(ssid) .setWpa2Passphrase(wpa2Passphrase) .build() val request = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .setNetworkSpecifier(specifier) .build() val networkCallback = object: ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { val networkSSID = wifiManager.connectionInfo.ssid .trim() .removeSurrounding("\"") if (networkSSID == "MY_NETWORK_WITHOUT_INTERNET_SSID") { // i'm connected here } } } connectivityManager.requestNetwork(request, networkCallback) } private fun connectPre10(ssid: String, key: String) { // setup wifi configuration val config = WifiConfiguration().apply { SSID = "\"$ssid\"" preSharedKey = "\"$key\"" } val networkId = wifiManager.addNetwork(config) wifiManager.disconnect() // disconnect from current (if connected) wifiManager.enableNetwork(networkId, true) // enable next attempt wifiManager.reconnect() }
请注意,为了读取网络ssid android,需要ACCESS_FINE_LOCATION权限,并且GPS必须处于活动状态。
[我立即注意到,使用程序化连接后,本机弹出窗口不再显示,但是在许多设备上,连接后,Android禁用了移动数据连接。
我想系统会希望这种行为,并且由以下事实决定:Android比计量连接更喜欢wifi。我可以,但是在无线网络无法访问互联网的情况下会怎样?需要连接的其他应用程序停止工作,因为它们无法访问Internet。
我需要一种解决方案,允许我的应用程序通过wifi和4g进行通信,而又不会阻止其他应用程序正常工作。
我的最小SDK API级别为23(棉花糖),定位为29(Android 10)。我设法通过保存来自连接管理器上注册的回调的网络来解决问题。
val connectivityManager by lazy { MyApplication.context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager } private val wifiNetworkCallback = object : ConnectivityManager.NetworkCallback() { // Called when the framework connects and has declared a new network ready for use. override fun onAvailable(network: Network) { super.onAvailable(network) listener?.onWifiConnected(network) } // Called when a network disconnects or otherwise no longer satisfies this request or callback. override fun onLost(network: Network) { super.onLost(network) listener?.onWifiDisconnected() } } private val mobileNetworkCallback = object : ConnectivityManager.NetworkCallback() { // Called when the framework connects and has declared a new network ready for use. override fun onAvailable(network: Network) { super.onAvailable(network) connectivityManager.bindProcessToNetwork(network) listener?.onMobileConnected(network) } // Called when a network disconnects or otherwise no longer satisfies this request or callback. override fun onLost(network: Network) { super.onLost(network) connectivityManager.bindProcessToNetwork(null) listener?.onMobileDisconnected() } } private fun setUpWifiNetworkCallback() { val request = NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .build() try { connectivityManager.unregisterNetworkCallback(wifiNetworkCallback) } catch (e: Exception) { Log.d(TAG, "WiFi Network Callback was not registered or already unregistered") } connectivityManager.requestNetwork(request, wifiNetworkCallback) } private fun setUpMobileNetworkCallback() { val request = NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .build() try { connectivityManager.unregisterNetworkCallback(mobileNetworkCallback) } catch (e: Exception) { Log.d(TAG, "Mobile Data Network Callback was not registered or already unregistered") } connectivityManager.requestNetwork(request, mobileNetworkCallback) }
标记此“ connectivityManager.bindProcessToNetwork()”,我们将在后面讨论。
随后我创建了两个不同的改造服务:
目前,在我的应用程序中,我可以重定向经过改造的流量,但是我包含在项目中的库如何理解它们应该使用哪个网络?答:他们不了解,实际上他们尝试使用wifi网络收到超时错误。
当我将google maps添加到我的应用程序时,我注意到了这一行为,画布仅显示一个空网格。由于无法通过我之前创建的公共改造服务来重定向Google地图流量,因此我不得不寻找另一种解决方案来解决此问题。
这里是您之前看到的ConnectivityManager.bindProcessToNetwork()!https://developer.android.com/reference/android/net/ConnectivityManager#bindProcessToNetwork(android.net.Network)。
通过这种方法,我可以告诉我的应用程序,将来创建的所有套接字都必须使用来自移动数据回调的网络。
使用此技巧,谷歌地图和我无法控制其连接的所有其他图书馆,他们将使用数据连接与互联网通信,因此它们将正常工作。
[在某些手机中,尤其是旧版android,仍然存在其他应用程序无法连接的问题,因为它们尝试使用wifi而不是使用移动数据。作为用户,如果使用一个应用程序,其他所有应用程序都无法使用,我会感到非常沮丧。
因此,我想了解是否有一种方法可以满足我的所有要求,无论我的应用程序是哪个版本,无论Android版本和手机供应商如何,其他应用程序都可以正常工作。
总而言之,我的问题是:
我有一个汽车伴侣应用程序,需要与wifi和移动数据网络进行通信。我的车辆控制单元提供了一个无法访问Internet的wifi网络,该网络公开了API ...
首先,关于您的问题: