Androidでの定期的なタスクのスケジュール


122

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

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

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

私が見たオプションは:

  1. タイマー

  2. ScheduledThreadPoolExecutor

  3. サービス

  4. BroadcastRecieverとAlarmManager

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

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

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

whatsappメッセージフィードバックメカニズムと同様: メッセージは配信されたようです

編集#2: AndroidトレーニングのVitalsセクションで読むことができるバッテリーの消耗の問題を防ぐために
JobSchedulerAPI を介して(またはFirebaseJobDispatcherより低いAPIの場合)定期的なタスクをほぼ常にスケジュールする必要があります

編集#3:
FirebaseJobDispatcherは非推奨になり、WorkSchedulerの機能も組み込んだWorkmanagerに置き換えられました。


2
AlarmManagerを使用したBroaccastReceiverの使い方は非常に簡単です。それは私が試した上記の選択肢の唯一のものです。

1
TimerをScheduledThreadPoolExecutorよりも使用する理由はほとんどありません。これは、複数のバックグラウンドスレッドを許可し、より優れた解決策(ms解決にのみ有用)を持ち、例外処理を可能にするため、より柔軟です。AlarmManagerに関しては、この投稿は違いについてのいくつかの情報を提供します。
アッシリア2013年

ライフサイクルが短い場合、つまり現在フォアグラウンドにあるアクティビティで30秒ごとにタスクを実行する場合は、ScheduledThreadPoolExecutor(またはTimer)を使用する方が効率的です。長期のライフサイクル、つまりバックグラウンドサービスで1時間ごとにいくつかのタスクを実行する場合、AlarmManagerを使用すると信頼性が向上します。
ヨーク

送信をスケジュールする必要があるのはなぜですか?アプリの説明から、リアルタイムで送信してみませんか?
iTech

ユーザーはタイムアウトを使用して、あなたがオンラインであると想定しているためです。つまり、「プレゼンス」または「タイピング」のメッセージを過去Xの時間内に受信しなかった場合、自動的にそれを実行していないものと
見なし

回答:


164

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

アラームマネージャー

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

注:アラームマネージャーは、アプリケーションが現在実行されていなくても、特定の時間にアプリケーションコードを実行させたい場合を対象としています。通常のタイミング操作(ティック、タイムアウトなど)では、ハンドラーを使用する方が簡単で効率的です。

タイマー

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分に1回が非常に頻繁であり、電力消費が少なすぎることに注意してください。


私は定期的なタスクのために、この方法を実行しようとしていますが、それは仕事していないようstackoverflow.com/questions/27872016/...
dowjones123

単純なものの場合-n秒ごとに状態をチェックするような-タイマーが行います。
IgorGanapolsky 2015年

1
@ Maid786 1週間の間隔または日数の期間でいくつかのタスク(通知の送信など)を実行する場合、何を使用する必要がありますか?アラームマネージャーは、そのためにあまりにも多くのバックグラウンド計算または処理を行いますか?
Chintan Shah、2015

30

タイマー

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

ScheduledThreadPoolExecutor

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

サービス

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

BroadcastRecieverとAlarmManager

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

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


1
それで、もし私がこのアプリを使いたいとしたら、そして30分ごとにバックアップを作りたいと思うでしょう。しかし、アプリが使用されていないときにバックアップを作成したくありません(それは完全に無駄になります)。Alarmmanagerは、再起動するまでアクションを繰り返し続けます(少なくとも、これは聞いたことです)。あなたは何をお勧めします?ScheduledThreadPoolExecutorまたはAlarmmanager?
hasdrubal 2013年

13

これは古い質問であり、回答済みであることがわかりましたが、これは誰かの役に立つかもしれません。あなたの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.

2

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

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

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


1

ユーザーが繰り返したいタスクをオンタイムタスクで作成し、カスタム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);
            }});
    }
}

}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.