TimerTask перестает стрелять в Сервис - PullRequest
0 голосов
/ 13 мая 2018

Мне нужно, чтобы в 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);
          }    
        }
    }
}

Должно ли это работать? IE я могу иметь простой Таймер в службе переднего плана, которая работает непрерывно, пока эта служба не остановлена? Если да, то есть ли ошибка в моем коде?

Я выбрал таймер, чтобы попытаться упростить его, мне когда-либо нужен был только один таймер, и я хотел, чтобы его можно было легко перепланировать. Я действительно понимаю, что могу попробовать Handler, ScheduledThreadPoolExecutor или даже AlarmManager. Я думал, что AlarmManager может быть излишним и истощать ресурсы, если он увольняет тонну. Не говоря уже о перепланировке.

Ответы [ 2 ]

0 голосов
/ 15 мая 2018

Вы должны использовать ScheduledExecutorService для того же. Существует много способов планирования фоновой задачи, например Диспетчер аварийных сигналов , JobDispatcher , Адаптер синхронизации , Планировщик заданий . Я предложу 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
    }
}
0 голосов
/ 13 мая 2018

Почему он не работает в фоновом режиме?

Это работает в фоновом режиме. Он не работает, когда устройство спит, так как процессор выключен. В Android «фон» означает «не имеет пользовательского интерфейса переднего плана» (активность, сервис с передним планом Notification).

Мне нужно, чтобы в Android работала служба, которая время от времени сохраняет значение в базе данных. Как часто это зависит от предпочтений пользователя, а также от того, произошли ли другие события, которые могут составлять от 30 секунд или до 30 минут.

То, что вы хотите, не было практичным на Android с 6.0.

Я думал, что AlarmManager может быть излишним и истощать ресурсы, если он увольняет тонну.

Это правда. Тем не менее, единственный способ заставить ваш существующий код работать - это получить частичную WakeLock, тем самым сохраняя работоспособность ЦП вечно. Это будет на порядок хуже по мощности, чем AlarmManager. И AlarmManager настолько плох, что с каждым выпуском Android, начиная с 6.0, все труднее становится использовать AlarmManager (или JobScheduler, или Timer и wakelock) для надежной работы.

Вам необходимо узнать, что такое Android и на что он не способен в отношении фоновой обработки, а затем соответствующим образом скорректировать свои планы продуктов. Эта тема слишком длинная для ответа переполнения стека.

Вот набор слайдов из презентации, которую я сделал в прошлом году на эту тему, с учетом Android 8.0 (используйте Пробел для перехода к следующему слайду). Вы также можете прочитать:

ИМХО, написание приложения, основанного на периодической фоновой обработке, в наше время является очень рискованным предприятием.

...