安排Android中的重复任务

时间:2013-01-17 10:01:49

标签: android scheduled-tasks

我正在设计一个应用程序,只要应用程序位于前台,该应用程序就会定期向专用服务器发送状态。

在我的网络搜索中,我看到了一些不同的方法,并想知道这样做的最佳方式。

安排服务器呼叫的最佳方式是什么?

我看到的选项是:

  1. Timer

  2. ScheduledThreadPoolExecutor

  3. Service

  4. 带有AlarmManager的BroadcastReciever。

  5. 你有什么看法?

    编辑:
    我需要这个的原因是基于聊天的应用程序将所有用户操作发送到远程服务器。
    即用户正在键入消息,用户正在阅读消息,用户在线,用户处于离线状态等。

    这意味着每隔一段时间,我就需要向服务器发送我正在做的事情,因为我和其他人打开聊天室,他们需要知道我在做什么。

    与whatsapp消息反馈机制类似: message looks delivered

    编辑#2:
    现在应该通过JobScheduler API(或FirebaseJobDispatcher用于较低的API)安排重复任务,以防止电池耗尽问题,可以在Android培训的vitals section中阅读< / p>

5 个答案:

答案 0 :(得分:157)

我不确定,但据我所知,我赞同我的观点。如果我错了,我总是接受最好的答案。

警报管理器

只要警报接收器的onReceive()方法正在执行,警报管理器就会保持CPU唤醒锁定。这可以保证在您完成广播处理之后手机不会睡眠。一旦onReceive()返回,警报管理器就会释放此唤醒锁定。这意味着在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.TimerScheduledThreadPoolExecutor(首选)来安排在后台线程上定期执行操作。

以下是使用后者的示例:

ScheduledExecutorService scheduler =
    Executors.newSingleThreadScheduledExecutor();

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

所以我更喜欢ScheduledExecutorService

但另外请注意,如果在您的应用程序运行时进行更新,您可以使用其他答案中建议的Timer或更新的ScheduledThreadPoolExecutor。 如果您的应用程序即使在未运行时也会更新,您应该使用AlarmManager

  

警报管理器适用于您希望在特定时间运行应用程序代码的情况,即使您的应用程序当前未运行。

请注意,如果您计划在应用程序关闭时进行更新,则每十分钟一次是非常频繁的,因此可能有点过于耗电。

答案 1 :(得分:30)

定时器

javadocs所述,您最好使用ScheduledThreadPoolExecutor。

的ScheduledThreadPoolExecutor

当您的用例需要多个工作线程且睡眠间隔很小时,请使用此类。多么小 ?好吧,我说约15分钟。 AlarmManager此时开始计划间隔,似乎表明对于较小的睡眠间隔,可以使用此类。我没有数据支持最后一个声明。这是一种预感。

服务

VM可随时关闭您的服务。不要将服务用于重复性任务。定期任务可以启动服务,这完全是另一回事。

带AlarmManager的BroadcastReciever

对于更长的睡眠间隔(> 15分钟),这是要走的路。 AlarmManager已经有常量(AlarmManager.INTERVAL_DAY),表明它可以在最初安排后几天触发任务。它还可以唤醒CPU以运行代码。

您应该根据您的时间和工作线程需求使用其中一种解决方案。

答案 2 :(得分:11)

我意识到这是一个老问题而且已经得到了回答,但这可以帮助某人。 在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.

答案 3 :(得分:2)

引用Scheduling Repeating Alarms - Understand the Trade-offs文档:

  

在应用的生命周期之外触发操作的常见方案是与服务器同步数据。在这种情况下,您可能会想要使用重复警报。但是,如果您拥有托管应用程序数据的服务器,则将Google云消息传递(GCM)与同步适配器结合使用是比AlarmManager更好的解决方案。同步适配器为您提供与AlarmManager相同的调度选项,但它为您提供了更大的灵活性。

因此,基于此,安排服务器调用的最佳方法是将Google Cloud Messaging (GCM)sync adapter结合使用。

答案 4 :(得分:1)

我已经创建了on time任务,其中用户想要重复的任务,在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);
            }});
    }
}

}