如何创建稳定Android后台服务实现每秒API拉取并解决UI卡顿问题
Android后台监控服务优化方案:解决UI卡顿与系统终止问题
需求:创建一个后台服务,每秒从API获取数据并存入数据库以实现数据监控。
现有问题:编写的MonitoringService在应用被杀死后仍可后台运行,但长时间使用后出现UI完全卡顿、无响应甚至被Android系统终止的问题。
现有代码:
public class MonitoringService extends Service { private static final int NOTIF_ID = 1; private static final String NOTIF_CHANNEL_ID = "Channel_Id"; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { startForeground(); getLiveUpdate(); AlramHendaler alramHendaler = new AlramHendaler(this); alramHendaler.setAlram(); return START_STICKY; } private void getLiveUpdate() { //I use TimerTask for execute thread in background ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); MyTimerTask myTimerTask = new MyTimerTask(() -> { //Here I get data every second from API Call<ArrayList<TempModel>> call = RetrofitClient.getInstance(ResponceData.BASE_URL_1).getMyApi().getData(url); call.enqueue(new Callback<ArrayList<TempModel>>() { @Override public void onResponse(Call<ArrayList<TempModel>> call, Response<ArrayList<TempModel>> response) { //get Response } @Override public void onFailure(Call<ArrayList<TempModel>> call, Throwable t) { } }); }); scheduler.scheduleAtFixedRate(myTimerTask, 0, 1, TimeUnit.SECONDS); } @SuppressLint("NewApi") private void startForeground() { Intent notificationIntent = new Intent(this, MainProcedureActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE); NotificationChannel chan = new NotificationChannel(NOTIF_CHANNEL_ID, "InOT", NotificationManager.IMPORTANCE_LOW); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Utills.addErrorInDatabase(this, "Service Is Connected"); assert manager != null; manager.createNotificationChannel(chan); startForeground(NOTIF_ID, new NotificationCompat.Builder(this, NOTIF_CHANNEL_ID) .setOngoing(true).setSmallIcon(R.drawable.ic_launcher_foreground) .setContentTitle(getString(R.string.app_name)).setContentText("Service is running background").setContentIntent(pendingIntent).build()); } @Override public void onDestroy() { super.onDestroy(); Log.i("EXIT", "ondestroy!"); Utills.addErrorInDatabase(this, "Service Is Disconnected"); Intent broadcastIntent = new Intent(this, CheckServiceBrodcast.class); sendBroadcast(broadcastIntent); } }
问题根源分析
- 线程资源泄漏:每次调用
getLiveUpdate()都会创建新的ScheduledExecutorService,未在服务销毁时关闭,导致线程持续占用CPU和内存,最终引发UI卡顿。 - 网络请求堆积:每秒发起一次API请求,若网络延迟或请求处理缓慢,会导致大量未完成的请求堆积,耗尽系统资源。
- 主线程阻塞:
Utills.addErrorInDatabase若在主线程执行数据库操作,会直接阻塞UI线程,引发卡顿。 - 重复触发逻辑:
AlarmHandler.setAlarm与ScheduledExecutorService的定时任务重复触发,导致任务执行次数翻倍,加重资源消耗。 - 后台服务合规性问题:Android 8.0+对后台服务限制严格,未正确管理服务生命周期,易被系统判定为低优先级进程终止。
针对性解决方案
一、解决UI卡顿问题
- 复用线程池并正确销毁:将
ScheduledExecutorService声明为全局变量,在onDestroy时调用scheduler.shutdown()终止所有定时任务,避免线程泄漏。 - 确保异步操作不阻塞主线程:所有数据库操作必须放在子线程执行,若
Utills.addErrorInDatabase未做异步处理,需修改为使用Executor或协程执行。 - 控制网络请求并发:改用
scheduleWithFixedDelay替代scheduleAtFixedRate,确保任务间隔基于上一次执行完成的时间,避免请求堆积;同时每次发起新请求前取消上一次未完成的请求。
二、解决系统终止问题
- 改用WorkManager实现后台任务:WorkManager是Android官方推荐的后台任务管理组件,自动适配不同Android版本的后台限制,能保证任务在系统重启、应用被杀死后仍可执行。
- 优化前台服务配置:保持前台服务通知的持续性,可适当提高通知优先级(如
IMPORTANCE_DEFAULT),避免系统误判为无用服务。 - 减少资源消耗:评估是否真的需要每秒拉取数据,若允许可适当延长间隔;或改用WebSocket实现实时数据推送,替代轮询,大幅降低资源占用。
- 避免内存泄漏:在服务销毁时取消所有未完成的Retrofit请求(调用
call.cancel()),解绑回调引用。
优化后的实现示例
方案1:基于WorkManager的推荐实现
1. 创建Worker类
public class MonitoringWorker extends Worker { private static final String TAG = "MonitoringWorker"; private final Retrofit retrofit; public MonitoringWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); retrofit = RetrofitClient.getInstance(ResponceData.BASE_URL_1); } @NonNull @Override public Result doWork() { // 执行API请求与数据库入库 try { Response<ArrayList<TempModel>> response = retrofit.getMyApi().getData(url).execute(); if (response.isSuccessful() && response.body() != null) { // 在子线程执行数据库操作 insertDataToDatabase(response.body()); } } catch (IOException e) { Log.e(TAG, "API请求失败: " + e.getMessage()); return Result.retry(); // 失败后重试 } return Result.success(); } private void insertDataToDatabase(ArrayList<TempModel> data) { Executors.newSingleThreadExecutor().execute(() -> { // 执行数据库入库逻辑 // ... }); } }
2. 启动周期性任务
// 在Activity或初始化类中启动任务 public void startMonitoringTask(Context context) { Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) // 仅在有网络时执行 .build(); PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(MonitoringWorker.class, 1, TimeUnit.SECONDS) .setConstraints(constraints) .build(); WorkManager.getInstance(context).enqueueUniquePeriodicWork( "MonitoringTask", ExistingPeriodicWorkPolicy.REPLACE, workRequest ); }
方案2:优化原Service实现
public class MonitoringService extends Service { private static final int NOTIF_ID = 1; private static final String NOTIF_CHANNEL_ID = "Channel_Id"; private ScheduledExecutorService scheduler; private Call<ArrayList<TempModel>> currentCall; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); // 初始化单线程调度器 scheduler = Executors.newSingleThreadScheduledExecutor(); startForeground(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { startMonitoringTask(); return START_STICKY; } private void startMonitoringTask() { scheduler.scheduleWithFixedDelay(() -> { // 取消上一次未完成的请求 if (currentCall != null && !currentCall.isCanceled()) { currentCall.cancel(); } // 发起新请求 currentCall = RetrofitClient.getInstance(ResponceData.BASE_URL_1).getMyApi().getData(url); currentCall.enqueue(new Callback<ArrayList<TempModel>>() { @Override public void onResponse(Call<ArrayList<TempModel>> call, Response<ArrayList<TempModel>> response) { if (response.isSuccessful() && response.body() != null) { // 异步执行数据库操作 insertDataToDatabase(response.body()); } } @Override public void onFailure(Call<ArrayList<TempModel>> call, Throwable t) { Log.e("MonitoringService", "请求失败: " + t.getMessage()); } }); }, 0, 1, TimeUnit.SECONDS); } private void insertDataToDatabase(ArrayList<TempModel> data) { Executors.newSingleThreadExecutor().execute(() -> { // 数据库入库逻辑 // ... // 异步记录日志 Utills.addErrorInDatabase(getApplicationContext(), "数据入库成功"); }); } @SuppressLint("NewApi") private void startForeground() { Intent notificationIntent = new Intent(this, MainProcedureActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE); NotificationChannel chan = new NotificationChannel(NOTIF_CHANNEL_ID, "监控服务", NotificationManager.IMPORTANCE_DEFAULT); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); assert manager != null; manager.createNotificationChannel(chan); // 异步执行数据库日志记录 Executors.newSingleThreadExecutor().execute(() -> { Utills.addErrorInDatabase(getApplicationContext(), "Service Is Connected"); }); startForeground(NOTIF_ID, new NotificationCompat.Builder(this, NOTIF_CHANNEL_ID) .setOngoing(true) .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentTitle(getString(R.string.app_name)) .setContentText("监控服务运行中") .setContentIntent(pendingIntent) .build()); } @Override public void onDestroy() { super.onDestroy(); // 关闭调度器 if (scheduler != null && !scheduler.isShutdown()) { scheduler.shutdownNow(); } // 取消当前请求 if (currentCall != null && !currentCall.isCanceled()) { currentCall.cancel(); } // 异步记录日志 Executors.newSingleThreadExecutor().execute(() -> { Utills.addErrorInDatabase(getApplicationContext(), "Service Is Disconnected"); }); Intent broadcastIntent = new Intent(this, CheckServiceBrodcast.class); sendBroadcast(broadcastIntent); } }
内容的提问来源于stack exchange,提问作者jcredking
相关产品推荐
相关产品推荐

