TimerTask在Service中停止触发

时间:2018-05-13 18:27:12

标签: android android-service android-timer

我需要在Android中运行一个服务,每隔一段时间就会将一个值存储到数据库中。根据用户偏好以及是否发生其他事件(通常为30秒或最多30分钟)的频率。

这不是用户隐藏的东西,实际上用户应该知道它的运行。因此,我认为前台服务可能是最好的方法。

我有一个正在运行的前台服务,它有一个TimerTask来计算它需要触发的频率。该服务很“粘”。所以应该坚持下去并且资源很少,操作系统应该在一段时间后重新启动它。

我的问题是,当应用程序背景化后,TimerTask似乎停止运行。

这是我的服务:

public class TimerService extends Service {

    private static final String LOG_NAME = TimerService.class.getName();
    private Timer timer;
    private final Handler timerHandler = new Handler();

    @Override
    public void onCreate() {
        super.onCreate();

        Notification notification = new NotificationCompat.Builder(this, "MY_APP_CHANNEL_ID")
                .setContentTitle("My Timer Service")
                .setContentText("Background timer task")
                .setSmallIcon(R.drawable.timer)
                .build();

        startForeground(1, notification);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startTimer();
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopTimer();
    }


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void stopTimer() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

    private void startTimer() {
        stopTimer();

        timer = new Timer();

        long frequency = // calculate frequency
        long delay = // calculate delay
        timer.scheduleAtFixedRate(new MyTimerTask(), delay, frequency);
    }

    private void saveToDatabase() {
        // Save some stuff to the database...
        if (some condition) {
          // might need to reschedule timer delay and frequency.
          startTimer();
        }
    }

    private class MyTimerTask extends TimerTask {
        @Override
        public void run() {
            timerHandler.post(new Runnable() {
                @Override
                public void run() {
                  onTimerFire();
                }
            });
        }

        private void onTimerFire() {
          try {
            saveToDatabase();
          } catch (Exception e) {
            Log.e(LOG_NAME, "Error in onTimerFire", e);
          }    
        }
    }
}

这应该有用吗?我可以在前台服务中有一个简单的计时器,它会一直触发,直到该服务停止为止?如果是,我的代码中有错误吗?

我选择了一个Timer来试图保持简单,我只需要一个计时器运行,我希望它能够轻松地重新安排。我确实意识到我可以尝试Handler,ScheduledThreadPoolExecutor,甚至是AlarmManager。我认为一个AlarmManager可能是过度杀伤,如果它发射了一吨就会耗尽资源。更不用说重新安排了。

2 个答案:

答案 0 :(得分:1)

  

为什么不在后台运行?

在后台运行。当设备处于休眠状态时,它不会运行,因为CPU已关闭。在Android中,“背景”仅表示“没有前景UI”(活动,具有前景Notification的服务)。

  

我需要在Android中运行一个服务,每隔一段时间就会将一个值存储到数据库中。根据用户偏好以及是否发生其他事件(通常为30秒或最多30分钟)的频率。

自从6.0以来,你想要的东西在Android上一直不实用。

  

我认为一个AlarmManager可能过度杀伤,如果它发射了一吨就会耗尽资源。

这是事实。但是,让现有代码工作的唯一方法是获取部分WakeLock,从而使CPU永远运行。对于功率而言,这将比AlarmManager更严重。并且AlarmManager非常糟糕,每个Android版本(从6.0开始)都会使AlarmManager(或JobScheduler,或Timer和wakelock)越来越难以使用做可靠的事。

您将需要了解Android在背景处理方面的能力和能力,然后相应地调整您的产品计划。对于Stack Overflow答案,该主题方式太长了。

Here is a set of slides来自我去年就此主题发布的演示文稿,考虑到Android 8.0(使用 Space 进入下一张幻灯片)。您可能还会阅读:

恕我直言,编写一个依赖于定期后台处理的应用程序是一个非常冒险的风险。

答案 1 :(得分:1)

应该使用ScheduledExecutorService。可以通过多种方式安排后台任务,例如Alarm ManagerJobDispatcherSync AdapterJob Scheduler。我会建议他们ScheduledExecutorService

我有一个在服务中使用ScheduledExecutorService的好例子。 (目前在高度优化的位置同步服务中使用)

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Created by KHEMRAJ on 1/29/2018.
 */

public class SyncService extends Service {
    private Thread mThread;
    ScheduledExecutorService worker;
    private static final int SYNC_TIME = 60 * 1000; // 60 seconds

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        startSyncService();
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopThread();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void stopThread() {
        worker = null;
        if (mThread != null && mThread.isAlive()) mThread.interrupt();
    }

    private void startSyncService() {
        if (worker == null) worker = Executors.newSingleThreadScheduledExecutor();
        if (mThread == null || !mThread.isAlive()) {
            mThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    saveToDb();
                    if (worker != null) {
                        worker.schedule(this, SYNC_TIME, TimeUnit.MILLISECONDS);
                    }
                }
            });
            mThread.start();
        }
    }


    private void saveToDb() {
        // TODO: 5/15/2018
    }
}