You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建稳定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);
 }
}

问题根源分析

  1. 线程资源泄漏:每次调用getLiveUpdate()都会创建新的ScheduledExecutorService,未在服务销毁时关闭,导致线程持续占用CPU和内存,最终引发UI卡顿。
  2. 网络请求堆积:每秒发起一次API请求,若网络延迟或请求处理缓慢,会导致大量未完成的请求堆积,耗尽系统资源。
  3. 主线程阻塞:Utills.addErrorInDatabase若在主线程执行数据库操作,会直接阻塞UI线程,引发卡顿。
  4. 重复触发逻辑:AlarmHandler.setAlarm与ScheduledExecutorService的定时任务重复触发,导致任务执行次数翻倍,加重资源消耗。
  5. 后台服务合规性问题: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 12:15:27