125

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

ウェブ全体を検索したところ、いくつかの異なるアプローチが見られたので、これを行う最善の方法は何かを知りたいと思いました.

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

私が見たオプションは次のとおりです。

  1. タイマー

  2. ScheduledThreadPoolExecutor

  3. サービス

  4. AlarmManagerを使用したBroadcastReciever 。

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

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

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

whatsapp メッセージのフィードバック メカニズムに似ています。 メッセージが配信されたように見える

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

編集 #3:
FirebaseJobDispatcher は廃止され、 JobScheduler の機能も組み込まれているWorkmanagerに置き換えられました。

4

5 に答える 5

167

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

アラームマネージャー

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

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

タイマー

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回の頻度が非常に高いため、消費電力が少なすぎる可能性があることに注意してください。

于 2013-01-17T11:17:08.787 に答える
30

タイマー

javadocsで述べたように、ScheduledThreadPoolExecutorを使用することをお勧めします。

ScheduledThreadPoolExecutor

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

サービス

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

AlarmManagerを使用したBroadcastReciever

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

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

于 2013-02-20T13:11:40.673 に答える
14

これは古い質問であり、回答済みですが、これは誰かを助けることができると思います. あなたの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.
于 2016-01-09T00:24:47.667 に答える
2

Scheduling Repeating Alarmsの引用- トレードオフのドキュメントを理解する:

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

したがって、これに基づいて、サーバー呼び出しをスケジュールする最良の方法は、同期アダプターと組み合わせてGoogle Cloud Messaging (GCM)を使用することです。

于 2014-12-23T12:55:37.937 に答える
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);
            }});
    }
}

}

于 2015-04-13T12:57:51.967 に答える