web-dev-qa-db-ja.com

Androidでの定期的なタスクのスケジューリング

アプリがフォアグラウンドにある限り、専用サーバーにプレゼンスを送信するという定期的なタスクを持つアプリを設計しています。

Web全体での検索で、いくつかの異なるアプローチを見て、これを行う最善の方法を知りたいと思いました。

サーバー呼び出しをスケジュールする最良の方法は何ですか?

私が見たオプションは:

  1. タイマー

  2. ScheduledThreadPoolExecutor

  3. サービス

  4. BroadcastRecieverと AlarmManager

あなたの意見は何ですか?

編集:
これが必要な理由は、すべてのユーザーアクションをリモートサーバーに送信するチャットベースのアプリのためです。
i.eユーザーはメッセージを入力している、ユーザーはメッセージを読んでいる、ユーザーはオンライン、ユーザーはオフラインなど.

これは、他の人とチャットルームを開くので、間隔ごとに1回、私がしていることをサーバーに送信する必要があることを意味します。

Whatsappメッセージフィードバックメカニズムと同様: message looks delivered

編集#2:
定期的なタスクは、ほぼ常にJobScheduler API(または下位APIの場合は FirebaseJobDispatcher )を介してスケジュールする必要があります。これにより、 vitalsセクション of Androidトレーニング

115
thepoosh

確信はありませんが、私の知る限りでは、意見を共有しています。私が間違っている場合、私は常にベストアンサーを受け入れます。

アラームマネージャー

アラームマネージャーは、アラームレシーバーのonReceive()メソッドが実行されている限り、CPUウェイクロックを保持します。これにより、ブロードキャストの処理が完了するまで電話がスリープしないことが保証されます。 onReceive()が戻ると、Alarm Managerはこのウェイクロックを解除します。これは、onReceive()メソッドが完了するとすぐに電話がスリープすることを意味します。アラームレシーバーがContext.startService()を呼び出した場合、要求されたサービスが開始される前に電話がスリープする可能性があります。これを防ぐには、BroadcastReceiverServiceで個別のウェイクロックポリシーを実装し、サービスが利用可能になるまで電話機が動作し続けるようにする必要があります。

注:アラームマネージャーは、アプリケーションが現在実行されていない場合でも、特定の時間にアプリケーションコードを実行する場合を対象としています。通常のタイミング操作(ティック、タイムアウトなど)では、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を使用する必要があります。

Alarm Managerは、アプリケーションが現在実行されていない場合でも、特定の時間にアプリケーションコードを実行したい場合を対象としています。

アプリケーションがオフになっているときに更新を計画している場合、10分に1回は非常に頻繁に行われるため、電力消費が少し多すぎることに注意してください。

160
Md Maidul Islam

タイマー

javadocs で述べたように、ScheduledThreadPoolExecutorを使用したほうがよいでしょう。

ScheduledThreadPoolExecutor

ユースケースで複数のワーカースレッドが必要であり、スリープ間隔が短い場合に、このクラスを使用します。どれくらい小さい?さて、私は約15分と言います。 AlarmManagerは、この時点でスケジュール間隔を開始し、より短いスリープ間隔にはこのクラスを使用できることを示唆しているようです。最後の声明を裏付けるデータがありません。それは予感です。

サービス

サービスはVMによっていつでも閉じることができます。定期的なタスクにサービスを使用しないでください。定期的なタスクでは、サービスをstartできますが、これはまったく別の問題です。

AlarmManagerを使用したBroadcastReciever

より長いスリープ間隔(> 15分)の場合、これが目的です。 AlarmManagerには既に定数(AlarmManager.INTERVAL_DAY)があり、最初にスケジュールされてから数日後にタスクをトリガーできることを示唆しています。また、CPUを起動してコードを実行することもできます。

タイミングとワーカースレッドのニーズに基づいて、これらのソリューションのいずれかを使用する必要があります。

30
Deepak Bala

これは古い質問であり、回答済みですが、これは誰かに役立つ可能性があります。あなたの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.
12
Emzor

繰り返しアラームのスケジューリング-トレードオフを理解する docs:

アプリの有効期間外に操作をトリガーする一般的なシナリオは、データをサーバーと同期することです。これは、繰り返しアラームを使用したくなる場合があります。ただし、アプリのデータをホストしているサーバーを所有している場合、Google Cloud Messaging(GCM)を同期アダプターと組み合わせて使用​​することは、AlarmManagerよりも優れたソリューションです。同期アダプターは、AlarmManagerと同じスケジューリングオプションをすべて提供しますが、柔軟性が大幅に向上します。

したがって、これに基づいて、サーバー呼び出しをスケジュールする最適な方法は、 Googleクラウドメッセージング(GCM)同期アダプター と組み合わせて使用​​することです。

2
tato.rodrigo

ユーザーが繰り返したいタスクを時間通りに作成し、カスタム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);
            }});
    }
}

}

1
hitesh141