Планирование повторяющейся задачи в Android

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

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

Каков наилучший способ запланировать вызов сервера?

Параметры, которые я видел, были:

Каково ваше мнение?

EDIT:
Причина, по которой мне это нужно, - это приложение для чата, которое отправляет все действия пользователя на удаленный сервер.
т.е. пользователь вводит сообщение, пользователь читает сообщение, пользователь в сети, пользователь отключен и т.д.

Это означает, что после каждого интервала мне нужно отправить сервер, что я делаю, так как я открываю чат с другими людьми, им нужно знать, что я делаю.

Подобно механизму обратной связи сообщения Whatsapp: message looks delivered

РЕДАКТИРОВАТЬ № 2:
Повторяющиеся задачи теперь должны быть запланированы почти всегда через API JobScheduler (или FirebaseJobDispatcher для более низких API-интерфейсов), чтобы предотвратить проблемы с батареей как можно прочитать в разделе vitals обучения в Android

Ответ 1

Я не уверен, но, насколько мне известно, я разделяю свои взгляды. Я всегда принимаю лучший ответ, если я ошибаюсь.

Диспетчер аварийных сигналов

Диспетчер аварийных сигналов удерживает блокировку слежения за процессором, пока выполняется приемник onReceive(). Это гарантирует, что телефон не будет спать, пока вы не закончите работу с трансляцией. Как только onReceive() возвращается, диспетчер аварийных сигналов освобождает эту блокировку слежения. Это означает, что телефон в некоторых случаях будет спать, как только ваш метод onReceive() завершится. Если ваш приемник сигналов тревоги вызвал Context.startService(), возможно, что телефон будет спать до запуска запрошенной службы. Чтобы предотвратить это, вашим BroadcastReceiver и Service необходимо будет выполнить отдельную политику блокировки слежения, чтобы убедиться, что телефон продолжает работать до тех пор, пока услуга не станет доступной.

Примечание. Диспетчер аварийных сигналов предназначен для случаев, когда вы хотите, чтобы ваш код приложения выполнялся в определенное время, даже если ваше приложение в настоящее время не работает. Для обычных операций синхронизации (тиков, тайм-аутов и т.д.) Проще использовать Handler.

Таймер

timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {

        synchronized public void run() {

            \\ here your todo;
            }

        }}, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(1));

Timer имеет некоторые недостатки, решаемые ScheduledThreadPoolExecutor. Так что это не лучший выбор

ScheduledThreadPoolExecutor.

Вы можете использовать java.util.Timer или ScheduledThreadPoolExecutor (предпочтительно), чтобы планировать действие, которое происходит через регулярные интервалы в фоновом потоке.

Вот пример с последним:

ScheduledExecutorService scheduler =
    Executors.newSingleThreadScheduledExecutor();

scheduler.scheduleAtFixedRate
      (new Runnable() {
         public void run() {
            // call service
         }
      }, 0, 10, TimeUnit.MINUTES);

Поэтому я предпочел ScheduledExecutorService

Но подумайте об этом, если обновления будут выполняться во время работы вашего приложения, вы можете использовать Timer, как это предложено в других ответах, или более новый ScheduledThreadPoolExecutor. Если ваше приложение будет обновляться, даже если оно не запущено, вы должны пойти с AlarmManager.

Диспетчер аварийных сигналов предназначен для случаев, когда вы хотите, чтобы ваш код приложения выполнялся в определенное время, даже если ваше приложение в настоящее время не работает.

Обратите внимание, что если вы планируете обновлять, когда приложение отключено, то каждые 10 минут достаточно часты и, возможно, могут быть слишком энергоемкими.

Ответ 2

Таймер

Как упоминалось в javadocs, вам лучше использовать ScheduledThreadPoolExecutor.

ScheduledThreadPoolExecutor

Используйте этот класс, когда для вашего случая использования требуется несколько рабочих потоков, а интервал ожидания невелик. Как мало? Ну, я бы сказал, около 15 минут. В этот момент AlarmManager запускает интервалы времени и, как представляется, для меньших интервалов сна этот класс может использоваться. У меня нет данных, чтобы поддержать последнее утверждение. Это догадка.

Сервис

Ваша служба может быть закрыта в любое время VM. Не используйте службы для выполнения повторяющихся задач. Повторяющаяся задача может запустить сервис, что является другим вопросом.

BroadcastReciever с AlarmManager

При длительных спящих интервалах ( > 15 минут) это путь. AlarmManager уже имеет константы (AlarmManager.INTERVAL_DAY), предлагая, что он может запускать задачи через несколько дней после того, как он был запланирован. Он также может разбудить CPU для запуска вашего кода.

Вы должны использовать одно из этих решений на основе ваших потребностей времени и рабочего потока.

Ответ 3

Я понимаю, что это старый вопрос, на который был дан ответ, но это может помочь кому-то. В activity

private ScheduledExecutorService scheduleTaskExecutor;

В onCreate

  scheduleTaskExecutor = Executors.newScheduledThreadPool(5);

    //Schedule a task to run every 5 seconds (or however long you want)
    scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            // Do stuff here!

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // Do stuff to update UI here!
                    Toast.makeText(MainActivity.this, "Its been 5 seconds", Toast.LENGTH_SHORT).show();
                }
            });

        }
    }, 0, 5, TimeUnit.SECONDS); // or .MINUTES, .HOURS etc.

Ответ 4

Цитирование Планирование повторных тревог - понимание компромиссов docs:

Общим сценарием для запуска операции вне времени жизни вашего приложения является синхронизация данных с сервером. Это случай, когда у вас может возникнуть соблазн использовать повторяющийся сигнал. Но если у вас есть сервер, на котором размещаются данные вашего приложения, использование Cloud Cloud Messaging (GCM) в сочетании с адаптером синхронизации является лучшим решением, чем AlarmManager. Адаптер синхронизации предоставляет вам все те же параметры планирования, что и AlarmManager, но он предлагает вам значительно большую гибкость.

Итак, на основе этого лучший способ запланировать вызов сервера использует Google Cloud Messaging (GCM) в сочетании с адаптер синхронизации.

Ответ 5

Я создал во времени задачу, в которой задача, которую пользователь хочет повторить, добавить в метод CustomTask Run(). он успешно повторяется.

 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Timer;
 import java.util.TimerTask;

 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.TextView;
 import android.app.Activity;
 import android.content.Intent;

 public class MainActivity extends Activity {

     CheckBox optSingleShot;
     Button btnStart, btnCancel;
     TextView textCounter;

     Timer timer;
     MyTimerTask myTimerTask;

     int tobeShown = 0  ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    optSingleShot = (CheckBox)findViewById(R.id.singleshot);
    btnStart = (Button)findViewById(R.id.start);
    btnCancel = (Button)findViewById(R.id.cancel);
    textCounter = (TextView)findViewById(R.id.counter);
    tobeShown = 1;

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

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
        //singleshot delay 1000 ms
        timer.schedule(myTimerTask, 1000);
    }else{
        //delay 1000ms, repeat in 5000ms
        timer.schedule(myTimerTask, 1000, 1000);
    }

    btnStart.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View arg0) {


            Intent i = new Intent(MainActivity.this, ActivityB.class);
            startActivity(i);

            /*if(timer != null){
                timer.cancel();
            }

            //re-schedule timer here
            //otherwise, IllegalStateException of
            //"TimerTask is scheduled already" 
            //will be thrown
            timer = new Timer();
            myTimerTask = new MyTimerTask();

            if(optSingleShot.isChecked()){
                //singleshot delay 1000 ms
                timer.schedule(myTimerTask, 1000);
            }else{
                //delay 1000ms, repeat in 5000ms
                timer.schedule(myTimerTask, 1000, 1000);
            }*/
        }});

    btnCancel.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View v) {
            if (timer!=null){
                timer.cancel();
                timer = null;
            }
        }
    });

}

@Override
protected void onResume() {
    super.onResume();

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

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
        //singleshot delay 1000 ms
        timer.schedule(myTimerTask, 1000);
    }else{
        //delay 1000ms, repeat in 5000ms
        timer.schedule(myTimerTask, 1000, 1000);
    }
}


@Override
protected void onPause() {
    super.onPause();

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

}

@Override
protected void onStop() {
    super.onStop();

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

}

class MyTimerTask extends TimerTask {

    @Override
    public void run() {

        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = 
                new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a");
        final String strDate = simpleDateFormat.format(calendar.getTime());

        runOnUiThread(new Runnable(){

            @Override
            public void run() {
                textCounter.setText(strDate);
            }});
    }
}

}

Ответ 6

BroadcastReciever с помощью AlarmManager. Это лучший способ запланировать серверный вызов для андроидной защиты.

Ответ 7

Может быть, это не ответит на ваш вопрос, но совет для структуры вашего приложения. Что касается меня, гораздо проще использовать node JS с SOCKET.IO для таких приложений, как chat. И в режиме реального времени вам не нужно спрашивать сервер каждый раз. Там вы можете узнать больше о SOCET IO - http://socket.io/