思路一句话:
"把『系统网络连接成功』当成一个系统广播事件,注册一个 ConnectivityManager.NetworkCallback(或者 legacy 的 BroadcastReceiver),一旦收到 『已连接且可上网』的回调,就启动协程/线程去调你的 API。"
- 权限(AndroidManifest.xml)
xml
<!-- 普通权限,安装即授予 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 需要真正上网,别忘 INTERNET -->
<uses-permission android:name="android.permission.INTERNET" />
- 生命周期感知型 NetworkCallback(推荐)
kotlin
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
NetworkProbe.init(this)
}
}
object NetworkProbe {
private val cm by lazy { app.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
private lateinit var app: Application
fun init(app: Application) {
this.app = app
// 兼容 O 以下
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
registerNetworkCallback()
} else {
registerLegacyReceiver()
}
}
@TargetApi(Build.VERSION_CODES.N)
private fun registerNetworkCallback() {
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) // 已验证可上网
.build()
cm.registerNetworkCallback(
request,
object : ConnectivityManager.NetworkCallback() {
// 第一次连接或网络恢复都会走到这里
override fun onAvailable(network: Network) {
super.onAvailable(network)
callMyApi()
}
// 如果只想『从断网到联网』触发一次,可再覆写 onLost 做状态机
}
)
}
// 兼容低版本
private fun registerLegacyReceiver() {
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
app.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(c: Context, i: Intent) {
val info = cm.activeNetworkInfo
if (info?.isConnected == true) {
callMyApi()
}
}
}, filter)
}
// 真正去调接口
private fun callMyApi() {
// 网络回调里不能做重 IO,切到 IO 线程
CoroutineScope(Dispatchers.IO).launch {
try {
val rsp = RetrofitClient.api.ping() // 你的 Retrofit 接口
Log.d("NetworkProbe", "API 调用成功:$rsp")
// 如果只想成功一次,可在这里 unregister 或者置标志位
} catch (e: Exception) {
Log.e("NetworkProbe", "API 失败", e)
}
}
}
}
- 只想在『前台 Activity』里监听?
用LifecycleObserver把registerNetworkCallback搬进onResume,onPause里unregister即可,避免后台重复触发。
- 常见坑提示
- 不要把
onAvailable当成"Wi-Fi 连上了",它只代表系统选中的默认网络变更;
加上NET_CAPABILITY_VALIDATED可确保已经通过 204/443 探测,真正可上网。 - 7.0+ 静态注册
CONNECTIVITY_ACTION无效,必须动态注册或改用NetworkCallback。 - 回调线程是 系统 Binder 线程,不能直接做网络 IO,务必切协程/线程池。
- 如果用户处于 ** captive portal **(需要网页认证),
VALIDATED不会满足,API 也调不通,符合预期。
一句话总结
"注册 ConnectivityManager.NetworkCallback → 在 onAvailable() 且 NET_CAPABILITY_VALIDATED 时切 IO 线程调你的接口" 。