Android开机完成后Http请求失效问题排查与解决求助
问题描述
设备:小米Mi 9T(Android 10 API 29)
此前应用通过OkHttp调用Java后端服务正常,重启设备后,注册的BootCompletedReceiver触发正常,但服务器调用抛出超时异常:
java.net.SocketTimeoutException: failed to connect to mydomain/XXX.XXX.238.40 (port 80) from /192.168.1.9 (port XXXXXX) after 20000ms
Manifest中注册的Receiver:
<receiver android:enabled="true" android:exported="true" android:name="com.mysomain.receiver.BootCompletedReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON"/> <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/> </intent-filter> </receiver>
Receiver的实现逻辑:通过Timer定时轮询网络状态,判断连接后启动服务:
public class BootCompletedReceiver extends BroadcastReceiver { private static final String TAG = BootCompletedReceiver.class.getSimpleName(); private static long TIMER_INTERVAL = 10000; private Timer timer = new Timer(); public void onReceive(Context context, Intent intent) { timerStart(context); } private void timerStart(Context context) { this.timer.scheduleAtFixedRate(new TimerTask() { public void run() { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); boolean connected = activeNetworkInfo != null && activeNetworkInfo.isConnected(); if (connected) { timer.cancel(); context.startService(new Intent(context, StartWorksService.class)); } } }, 0, TIMER_INTERVAL); } }
问题原因
- 网络状态判断不准确:
activeNetworkInfo.isConnected()仅能判断设备与网络的物理连接状态,无法验证网络是否能正常访问公网。比如WiFi已连接但尚未完成IP分配、网络需要门户认证,或者系统后台网络限制导致请求无法发出,此时该API仍会返回true。 - API过时与系统限制:
getActiveNetworkInfo()在Android 10(API29)已被标记为过时,且Android 10加强了后台应用的网络访问限制,即使网络连接状态正常,后台应用也可能被限制发起网络请求。 - 小米MIUI的额外管控:MIUI有独立的后台权限策略,重启后应用处于后台状态,可能被默认限制了后台联网权限或自启动权限,导致服务启动后无法正常发起网络请求。
- Timer轮询的局限性:固定间隔的轮询无法精准捕捉网络真正可用的时机,可能在网络尚未完全就绪时就启动了服务,进而引发连接超时。
解决方案
1. 替换为精准的网络可用性监听
使用ConnectivityManager.NetworkCallback替代轮询,监听网络的真正可用性(而非仅连接状态):
public class BootCompletedReceiver extends BroadcastReceiver { private ConnectivityManager.NetworkCallback networkCallback; @Override public void onReceive(Context context, Intent intent) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); networkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(@NonNull Network network) { super.onAvailable(network); // 网络真正可用时启动服务 context.startService(new Intent(context, StartWorksService.class)); // 取消监听 cm.unregisterNetworkCallback(this); } }; // 注册网络监听,要求网络可访问公网 NetworkRequest request = new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) .build(); cm.registerNetworkCallback(request, networkCallback); } }
2. 适配Android 10+的权限与后台任务
- 在Manifest中添加必要权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> - 改用
WorkManager处理后台任务,它会自动适配系统的后台限制,并支持网络条件约束:
对应的Worker类:// 在Receiver中触发WorkManager public void onReceive(Context context, Intent intent) { Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) // 网络连接时执行 .build(); OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(StartWorksWorker.class) .setConstraints(constraints) .build(); WorkManager.getInstance(context).enqueue(workRequest); }public class StartWorksWorker extends Worker { public StartWorksWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } @NonNull @Override public Result doWork() { // 执行原StartWorksService中的任务逻辑 return Result.success(); } }
3. 适配小米MIUI权限
引导用户开启以下权限:
- 应用的「自启动权限」:小米手机设置→应用设置→应用管理→目标应用→权限管理→自启动,设置为允许。
- 应用的「后台联网权限」:同样在权限管理中找到后台联网,开启允许。
4. 优化OkHttp配置(可选)
适当调整超时时间并开启重试,应对网络不稳定场景:
OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .build();
内容的提问来源于stack exchange,提问作者araraujo
相关产品推荐
相关产品推荐

