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

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

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

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

Варианты, которые я видел, были:

  1. Таймер

  2. ScheduledThreadPoolExecutor.

  3. Сервис

  4. BroadcastReciever с AlarmManager.

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

РЕДАКТИРОВАТЬ:
Причина в том, что мне нужно это для приложения на основе чата, которое отправляет все действия пользователя на удаленный сервер.
т.е. пользователь печатает сообщение, пользователь читает сообщение, пользователь онлайн, пользователь оффлайн и т. д.

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

Аналогично механизму обратной связи в WhatsApp: сообщение выглядит доставленным

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

6 ответов

Решение

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

Диспетчер аварий

Диспетчер аварийных сигналов удерживает блокировку ЦП до тех пор, пока onReceive() метод выполняется. Это гарантирует, что телефон не будет спать, пока вы не закончили обработку трансляции. однажды onReceive() возвращается, Alarm Manager снимает эту блокировку. Это означает, что телефон в некоторых случаях будет спать, как только ваш 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,

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

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

таймер

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

ScheduledThreadPoolExecutor

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

обслуживание

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

BroadcastReciever с AlarmManager

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

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

Я понимаю, что это старый вопрос, и на него дан ответ, но это может кому-то помочь. В вашем 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.

Цитирование планирования повторяющихся сигналов тревоги - Понимание документов по компромиссам:

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

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

Я создал на время задачу, в которой задача, которую пользователь хочет повторить, добавить в метод Custom TimeTask 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);
            }});
    }
}

}

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

Другие вопросы по тегам