共计 3018 个字符,预计需要花费 8 分钟才能阅读完成。
一、为什么需要控制网络路由?
在开发即时通讯、在线支付或视频播放类应用时,常遇到这些典型场景:

- 用户开启 WiFi 但信号极差,实际连接速度不如蜂窝数据
- 双卡手机中某张 SIM 卡流量即将耗尽,需切换到副卡
- 后台同步大文件时希望仅使用 WiFi 避免消耗套餐流量
这些需求本质上都需要对网络路由进行精细化控制。传统粗暴的 uses-permission 权限声明只能解决 ” 能不能用网络 ” 的问题,而我们需要的是 ” 用哪个网络 ” 的精确控制能力。
二、新旧 API 对比:从 NetworkInfo 到 NetworkCallback
传统 NetworkInfo 方式(API 21 前)
// 已废弃的旧方法
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo wifiInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if(wifiInfo != null && wifiInfo.isConnected()) {// 使用 WiFi 网络}
缺陷分析:
- 轮询检查机制导致延迟感知网络变化
- 无法获取具体 Network 对象进行绑定
- 双卡场景下无法区分移动数据来源
现代 NetworkCallback 方案(API 21+)
val networkCallback = object : ConnectivityManager.NetworkCallback() {override fun onAvailable(network: Network) {
// 网络可用时触发
if(isWifi(network)) {bindProcessToNetwork(network)
}
}
}
// 注册监听
cm.registerNetworkCallback(NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build(),
networkCallback
)
优势体现:
- 事件驱动机制实时响应网络变化
- 可获取具体的 Network 实例进行绑定
- 支持基于传输类型 (TRANSPORT_CELLULAR/WIFI) 的精细过滤
三、核心代码实现
强制使用指定网络类型
fun bindToWifiOnly() {val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
val wifiNetworks = cm.allNetworks.filter { network ->
cm.getNetworkCapabilities(network)
?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ?: false
}
wifiNetworks.firstOrNull()?.let {cm.bindProcessToNetwork(it)
// 或者针对特定请求绑定
// cm.setProcessDefaultNetwork(it)
}
}
兼容性处理方案
// 兼容 Android 5.0 以下版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {// 使用 NetworkCallback 方案} else {
// 降级为传统 NetworkInfo 检测
NetworkInfo activeInfo = cm.getActiveNetworkInfo();
if(activeInfo != null && activeInfo.getType() == ConnectivityManager.TYPE_WIFI) {// 旧版本无法强制绑定,只能提示用户}
}
四、性能优化要点
网络切换延迟处理
- 设置合理的
NETWORK_VALIDATION_TIMEOUT_MS(默认 2 秒) - 实现指数退避重试机制:
private var retryDelay = 1000L
fun attemptNetworkRequest() {
try {// 执行网络请求} catch (e: IOException) {
handler.postDelayed({
retryDelay *= 2
attemptNetworkRequest()}, min(retryDelay, 30000L)) // 最大延迟 30 秒
}
}
电量消耗控制
- 避免持续调用
requestNetwork()(会保持射频活跃) - 后台任务使用
registerNetworkCallback()代替 - 按需注销监听器:
override fun onPause() {super.onPause()
connectivityManager.unregisterNetworkCallback(networkCallback)
}
五、生产环境避坑指南
机型兼容问题
- 华为 EMUI 可能限制
bindProcessToNetwork - 小米设备需要额外申请
CHANGE_NETWORK_STATE权限 - 三星双卡手机需注意
getNetworkInfo()返回的主副卡顺序
后台服务处理
-
使用
JobScheduler设置网络约束:<job-info> <required-network android:networkType="unmetered"/> </job-info> -
Foreground Service 中动态更新 Notification 显示当前网络状态
权限管理
-
危险权限需要运行时申请:
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -
Android 10+ 需要添加:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
六、进阶思考方向
结合 WorkManager 的智能切换
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresBatteryNotLow(true)
.build()
val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>()
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueue(uploadWork)
IoT 设备特殊场景
- 使用
ConnectivityManager#requestNetwork创建专属网络通道 - 针对 BLE/WiFi Direct 等特殊传输类型定制策略
- 考虑实现自定义的
NetworkSpecifier
写在最后
网络路由控制就像城市交通调度系统,既要保证数据包能到达目的地,又要选择最优路径。随着 5G 和 WiFi6 的普及,多网协同将成为趋势。建议在实际项目中先做好网络质量探测(如 ping 延迟测试),再结合业务需求制定动态切换策略。完整的示例代码已放在 GitHub 仓库中,欢迎交流讨论。
正文完
