【Android】 连接wifi时,强制应用使用流量
【Android】 连接wifi时,强制应用使用流量
两种方法,一种是绑定整个进程,一种是让部分请求可以走流量。
方法一:绑定整个应用进程
Android开发实战:Wi-Fi连接下强制App使用移动数据
在特定的业务场景下,例如执行高优先级的网络任务或测试不同网络环境,我们可能需要在Wi-Fi已连接的情况下,强制让应用通过移动数据(蜂窝网络)进行通信。本文将介绍如何在原生Android开发中实现这一高级功能。核心原理
自 Android 5.0 (API 21) 起,ConnectivityManager
提供了多网络连接管理API。其核心思想是:应用可以向系统请求一个特定类型的网络(如蜂窝网络),获取该网络的 Network
对象引用,然后将应用的全部或部分网络通信绑定到这个特定的 Network
对象上,从而绕过系统默认的Wi-Fi网络。
方法一:绑定整个应用进程(全局生效)
这是最直接的方法,它会使应用内的所有标准网络请求(OkHttp, Retrofit等)都切换到移动数据。
第1步:添加权限
在 AndroidManifest.xml
中确保具备网络权限:
`<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />`
第2步:请求并绑定网络
创建一个管理类来封装网络切换逻辑。
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Buildclass CellularNetworkManager(context: Context) {private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManagerprivate var networkCallback: ConnectivityManager.NetworkCallback? = null/*** 请求蜂窝网络并绑定当前应用进程。*/fun bind(onSuccess: () -> Unit, onError: (String) -> Unit) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {onError("Feature requires Android API 21+.")return}// 构建一个蜂窝网络请求val request = NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build()networkCallback = object : ConnectivityManager.NetworkCallback() {// 当蜂窝网络可用时override fun onAvailable(network: Network) {try {// 核心:将进程绑定到此网络connectivityManager.bindProcessToNetwork(network)onSuccess()} catch (e: Exception) {onError("Failed to bind: ${e.message}")}}// 当找不到合适的网络时override fun onUnavailable() {onError("Cellular network unavailable.")}}connectivityManager.requestNetwork(request, networkCallback!!)}/*** 解除绑定,恢复使用系统默认网络。*/fun unbind() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {connectivityManager.bindProcessToNetwork(null) // 传 null 解除绑定networkCallback?.let { connectivityManager.unregisterNetworkCallback(it) }networkCallback = null}}
}
第3步:在Activity/ViewModel中使用
// 在Activity中
private val cellularManager = CellularNetworkManager(this)// 绑定
cellularManager.bind(onSuccess = { // 绑定成功,现在发起的网络请求将通过流量Log.d("Network", "Process successfully bound to cellular network.")},onError = { errorMsg -> Log.e("Network", "Binding failed: $errorMsg") }
)// 在不再需要时,务必解除绑定,例如在 onDestroy() 中
override fun onDestroy() {cellularManager.unbind()super.onDestroy()
}
方法二:为特定请求(如OkHttp)指定网络(精准控制)
如果你只想让某一部分请求走流量,可以为 OkHttp
等网络库的客户端实例单独配置网络。
1. 获取Network
对象
首先,通过 requestNetwork
获取到 Network
对象,但不要调用 bindProcessToNetwork
。
// 在 onAvailable(network: Network) 回调中
// 我们得到了 network 对象,用它来配置 OkHttp
val cellularOkHttpClient = createOkHttpClientFor(network)
// 使用这个 client 发起请求
2. 创建绑定的OkHttpClient
利用 Network
对象提供的 socketFactory
来构建 OkHttpClient
。
import okhttp3.OkHttpClientfun createOkHttpClientFor(network: Network): OkHttpClient {return OkHttpClient.Builder().socketFactory(network.socketFactory).build()
}
使用这个 cellularOkHttpClient
实例发起的请求将只通过蜂窝网络,而应用中其他常规的 OkHttpClient
实例则继续使用默认的Wi-Fi网络。
以上都是AI生成,写这篇文档的原因是:我使用的是方法二,按照最初方案并没有成功连接网路,而是需要添加DNS
补充:
val cellularDns = object : Dns {override fun lookup(hostname: String): List<InetAddress> {return try {cellularNetwork?.getAllByName(hostname)?.toList() ?: emptyList()} catch (e: UnknownHostException) {throw e}}}fun createOkHttpClientFor(network: Network): OkHttpClient {return OkHttpClient.Builder().socketFactory(network.socketFactory).dns(cellularDns).build()
}