如何在固定时间间隔后重复执行异步任务

59

如何使异步任务每隔一段时间执行一次,就像定时器一样...实际上,我正在开发一个应用程序,它将自动下载来自服务器的所有最新未读问候,并且为此我必须在一些固定的时间间隔后从服务器检查更新....我知道可以很容易地通过计时器实现,但我想使用异步任务,因为我认为它对Android应用程序更有效。

5个回答

123
public void callAsynchronousTask() {
    final Handler handler = new Handler();
    Timer timer = new Timer();
    TimerTask doAsynchronousTask = new TimerTask() {       
        @Override
        public void run() {
            handler.post(new Runnable() {
                public void run() {       
                    try {
                        PerformBackgroundTask performBackgroundTask = new PerformBackgroundTask();
                        // PerformBackgroundTask this class is the class that extends AsynchTask 
                        performBackgroundTask.execute();
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                    }
                }
            });
        }
    };
    timer.schedule(doAsynchronousTask, 0, 50000); //execute in every 50000 ms
}

3
异步任务始终在自己的线程上运行,那么为什么要在处理程序上运行它? - Siddharth
9
异步任务确实在单独的线程上运行,但不能从除了UI线程之外的其他线程启动。我猜处理程序是为了允许这样做的。 - r1k0
6
如果使用这种技术启动AsyncTasks的Activity被杀死(如屏幕旋转或类似接听电话的操作系统中断),就会导致悬空引用问题。因此,如果您愿意在这些情况下让程序崩溃,那么请放心使用这种方法。 - SMBiggs
1
@ScottBiggs 哪种技术更好/不会崩溃? - colti
2
@colti:还没有找到好的解决方案。除了非常简单的东西外,我已经放弃使用ASyncTasks。对于更复杂的操作,我使用服务(是的,我知道,开销很烦人,但至少它不会那么容易崩溃。只需要小心清理并不留下悬空服务)。 - SMBiggs
显示剩余4条评论

6
  //Every 10000 ms   
       private void doSomethingRepeatedly() {
      Timer timer = new Timer();
      timer.scheduleAtFixedRate( new TimerTask() {
            public void run() {

                  try{

                     new SendToServer().execute(); 

                  }
                  catch (Exception e) {
                      // TODO: handle exception
                  }

             }
            }, 0, 10000);
                     }

2
在Android文档中,建议所有新代码优先使用ScheduledThreadPoolExecutor而不是Timer。https://developer.android.com/reference/java/util/Timer.html - Martin O'Shea

2
您可以使用处理程序:
private int m_interval = 5000; // 5 seconds by default, can be changed later
private Handle m_handler;

@Override
protected void onCreate(Bundle bundle)
{
  ...
  m_handler = new Handler();
}

Runnable m_statusChecker = new Runnable()
{
     @Override 
     public void run() {
          updateStatus(); //this function can change value of m_interval.
          m_handler.postDelayed(m_statusChecker, m_interval);
     }
}

void startRepeatingTask()
{
    m_statusChecker.run(); 
}

void stopRepeatingTask()
{
    m_handler.removeCallback(m_statusChecker);
}

但我建议您检查这个框架:http://code.google.com/intl/de-DE/android/c2dm/。这是一种不同的方法:当有东西准备好时,服务器会通知手机(从而节省带宽和性能:))。


非常感谢。实际上,我只是开发应用程序的客户端部分。服务器端已经为iPhone开发的同一应用程序而工作,我必须使用相同的服务器来为Android开发。 - Waseem
1
我对Android中的线程很陌生。你在哪里将可运行对象传递给处理程序? - Dheeraj Bhaskar
2
回答@DheeB,回答者在这里没有提到,尽管应该在实例化时像这样m_handler = new Handler(m_statusChecker)。另一个原因是这个解决方案可能不起作用,因为问题明确表明将有网络操作“自动从服务器下载所有最新的未读问候语”..然而,尽管您正在使用处理程序,但此处理程序/可运行文件仍在UI线程中运行,仍然会阻塞。您需要手动在单独的线程中启动它。 - tony9099

2
创建一个服务并通过闹钟管理器进行调度,这样不是更有效吗?

1
创建一个服务很麻烦,有很多事情要在服务上处理。我宁愿使用一个计时器。 - Siddharth
2
服务易于启动和停止。此外,它们不与UI线程绑定。所以,是的,我会使用服务。 - IgorGanapolsky
@IgorGanapolsky 是的,它们确实是。但是它们也很麻烦,如果一切都可以通过服务完成,为什么会创建asynctask、timer和这些模块来处理较小的操作呢? - tony9099
1
@tony9099 AsyncTask旨在在完成后更新UI线程,而Service则不是。至于Timer-它既不属于这里也不属于那里-它与AsyncTask和Service之间的比较无关。 - IgorGanapolsky
@IgorGanapolsky 没错,但是如果你仔细阅读问题,用户想要在服务器上检查一些内容,获取并更新UI;这是asynctask的理想场景。 - tony9099
2
我同意Igor的观点。由于Activity在设备旋转(以及其他时候)被杀死的问题,AsyncTasks除非非常小心地处理(而这些示例中并没有),否则会导致崩溃。真正的解决方案是咬紧牙关(是的,我知道,这很痛苦),使用Services。 - SMBiggs

1
接受的答案有问题。使用TimerTask()通过handler激活异步任务是一个糟糕的想法。在方向改变时,您必须记得取消计时器和处理程序调用。如果不这样做,它将在每次旋转时再次调用异步任务。这将导致应用程序爆炸服务器(如果这是rest http get请求)而不是X时间 - 最终每秒钟的调用将成为许多调用。(因为根据屏幕旋转的数量会有很多计时器)。如果活动和后台线程中运行的任务很重,则可能会崩溃应用程序。 如果您使用计时器,则将其设置为类成员并在onStop()中取消。
            TimerTask mDoAsynchronousTask;


            @Override
            public void onStop(){
               super.onStop();                 
               mDoAsynchronousTask.cancel();
               mHandler.removeCallbacks(null);
               ... 
            }


          public void callAsynchronousTask(final boolean stopTimer) {
             Timer timer = new Timer();
             mDoAsynchronousTask = new TimerTask() {
                 @Override
                 public void run() {
                     mHandler.post(new Runnable() {
                 ...

尽量避免使用异步任务,如果必须使用,则使用调度程序服务来运行异步任务。或者使用应用程序类,例如这个好主意: https://fattybeagle.com/2011/02/15/android-asynctasks-during-a-screen-rotation-part-ii/

或者使用简单的处理程序(不使用定时器,只使用postDelayed),并且在onStop()中调用cancel异步任务是一个好习惯。这段代码使用postDelayed很好地工作:

           public class MainActivity extends AppCompatActivity {

                  MyAsync myAsync = new MyAsync();

                  private final Handler mSendSSLMessageHandler = new Handler();
                  private final Runnable mSendSSLRunnable = new Runnable(){

                  ..


                 @Override
                 protected void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    setContentView(R.layout.activity_main);
                    ConnectivityManager connMgr = (ConnectivityManager)   
                    getSystemService(Context.CONNECTIVITY_SERVICE);
                    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
                    if (networkInfo != null && networkInfo.isConnected()) {
                            mSendSSLMessageHandler.post(mSendSSLRunnable);
                    }else
                    ..

                  @Override
                  public void onStop(){
                   super.onStop();
                      if ( progressDialog!=null && progressDialog.isShowing() ){
                           progressDialog.dismiss();
                      }
                    mSendSSLMessageHandler.removeCallbacks(mSendSSLRunnable);
                    myAsync.cancel(false);
                   }


              private final Runnable mSendSSLRunnable = new Runnable(){
              @Override
                public void run(){
                   try {
                    myAsync = new MyAsync();
                    myAsync.execute();
                   } catch (Exception e) {
                      // TODO Auto-generated catch block
                   }
                   mSendSSLMessageHandler.postDelayed(mSendSSLRunnable, 5000);
               }
          };


          class MyAsync extends AsyncTask<Void, Void, String> {
                boolean running = true;

                @Override
                protected void onPreExecute() {
                super.onPreExecute();
                  progressDialog = ProgressDialog.show               
                  (MainActivity.this, "downloading", "please wait");
                }

              @Override
              protected String doInBackground(Void... voids) {
                 if (!running) {
                       return null;
                  }
                 String result = null;
                 try{
                 URL url = new URL("http://192...");
                 HttpURLConnection urlConnection = (HttpURLConnection)            
                 url.openConnection();
                 InputStream in = new BufferedInputStream (urlConnection.getInputStream());
                 result = inputStreamToString(in);
                }catch(Exception e){
                   e.printStackTrace();
                }

               return result;
           }


    @Override
    protected void onCancelled() {
        boolean running = false;
    }
    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        progressDialog.dismiss();
        try {

              ..


        } catch (JSONException e) {
            textView.append("json is invalid");
            e.printStackTrace();
        }

    }


}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接