【问题标题】:Android - Setting a Timeout for an AsyncTask?Android - 为 AsyncTask 设置超时?
【发布时间】:2011-12-14 12:39:37
【问题描述】:

我执行了一个 AsyncTask 类,它从网站下载大量数据。

如果最终用户在使用时数据连接非常缓慢或不稳定,我想在一段时间后使AsyncTask超时。我的第一种方法是这样的:

MyDownloader downloader = new MyDownloader();
downloader.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
  @Override
  public void run() {
      if ( downloader.getStatus() == AsyncTask.Status.RUNNING )
          downloader.cancel(true);
  }
}, 30000 );

启动AsyncTask 后,将启动一个新的处理程序,如果AsyncTask 仍在运行,它将在30 秒后取消它。

这是一个好方法吗?或者AsyncTask 中是否有更适合此目的的内容?

【问题讨论】:

  • 在尝试了几种不同的方法后,我得出结论认为您的问题应该是公认的答案。
  • 感谢您提出的问题,我真的遇到了同样的问题,您的代码帮助了我 +1
  • 你的问题本身就是一个很好的答案+50 :)
  • 这个处理程序是连续运行还是一次运行?

标签: java android android-asynctask


【解决方案1】:

是的,有AsyncTask.get()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

请注意,通过在主线程(AKA.UI 线程)中调用它会阻塞执行,您可能需要在单独的线程中调用它。

【讨论】:

  • 如果此超时方法在主 UI 线程上运行,似乎它违背了使用 AsyncTask 开始的目的......为什么不只使用我在问题中描述的 handler 方法?看起来更简单,因为 UI 线程在等待运行 Handler 的 Runnable 时不会冻结...
  • 好的,谢谢,我只是不确定是否有合适的方法。
  • 我不明白你为什么在另一个线程中调用 get()。它看起来很奇怪,因为 AsyncTask 本身就是一种线程。我觉得 Jakobud 的方案比较OK。
  • 我认为 .get() 类似于posix线程的join,目的是等待线程完成。在您的情况下,它用作超时,这是一个间接属性。这就是为什么您需要从处理程序或另一个线程调用 .get() 以避免主 UI 阻塞
  • 我知道这是几年后的事了,但这仍然没有内置到android中,所以我做了一个支持类。我希望它可以帮助某人。 gist.github.com/scottTomaszewski/…
【解决方案2】:

在 onPreExecute() 方法的 AsyncTask 的扩展类中使用 CountDownTimer 类:

主要优势,Async 监控在类内部完成。

public class YouExtendedClass extends AsyncTask<String,Integer,String> {
...
public YouExtendedClass asyncObject;   // as CountDownTimer has similar method -> to prevent shadowing
...
@Override
protected void onPreExecute() {
    asyncObject = this;
    new CountDownTimer(7000, 7000) {
        public void onTick(long millisUntilFinished) {
            // You can monitor the progress here as well by changing the onTick() time
        }
        public void onFinish() {
            // stop async task if not in progress
            if (asyncObject.getStatus() == AsyncTask.Status.RUNNING) {
                asyncObject.cancel(false);
                // Add any specific task you wish to do as your extended class variable works here as well.
            }
        }
    }.start();
...

例如更改 CountDownTimer(7000, 7000) -> CountDownTimer(7000, 1000),它会在调用 onFinish() 之前调用 onTick() 6 次。如果您想添加一些监控,这很好。

感谢我在此页面中得到的所有好建议 :-)

【讨论】:

    【解决方案3】:

    在这种情况下,您的下载器是基于 URL 连接的,您有许多参数可以帮助您在没有复杂代码的情况下定义超时:

      HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
    
      urlc.setConnectTimeout(15000);
    
      urlc.setReadTimeout(15000);
    

    如果你只是把这段代码带入你的异步任务,没问题。

    'Read Timeout'是为了在传输过程中测试一个坏的网络。

    'Connection Timeout' 仅在开始时调用以测试服务器是否启动。

    【讨论】:

    • 我同意。使用这种方法更简单,它实际上会在达到超时时终止连接。使用 AsyncTask.get() 方法时,您只会被告知已达到时间限制,但下载仍在处理中,并且实际上可能会在稍后完成 - 导致代码更加复杂。谢谢。
    • 请注意,在非常慢的互联网连接上使用大量线程时,这还不够。更多信息:leakfromjavaheap.blogspot.nl/2013/12/…thushw.blogspot.nl/2010/10/…
    【解决方案4】:

    我不认为 AsyncTask 中内置了类似的东西。您的方法似乎是一个很好的方法。只要确保定期检查 AsyncTask 的 doInBackground 方法中 isCancelled() 的值,以便在 UI 线程取消它时结束此方法。

    如果您出于某种原因想避免使用处理程序,您可以在 AsyncTask 中定期检查 System.currentTimeMillis 并在超时时退出,尽管我更喜欢您的解决方案,因为它实际上可以中断线程。

    【讨论】:

      【解决方案5】:
               Context mContext;
      
               @Override
               protected void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                                          mContext = this;
      
                  //async task
                  final RunTask tsk = new RunTask (); 
                  tsk.execute();
      
                  //setting timeout thread for async task
                  Thread thread1 = new Thread(){
                  public void run(){
                      try {
                          tsk.get(30000, TimeUnit.MILLISECONDS);  //set time in milisecond(in this timeout is 30 seconds
      
                      } catch (Exception e) {
                          tsk.cancel(true);                           
                          ((Activity) mContext).runOnUiThread(new Runnable()
                          {
                               @SuppressLint("ShowToast")
                              public void run()
                               {
                                  Toast.makeText(mContext, "Time Out.", Toast.LENGTH_LONG).show();
                                  finish(); //will close the current activity comment if you don't want to close current activity.                                
                               }
                          });
                      }
                  }
              };
              thread1.start();
      
               }
      

      【讨论】:

      • 考虑添加一些解释
      【解决方案6】:

      您可以再添加一个条件以使取消更加稳健。例如,

       if (downloader.getStatus() == AsyncTask.Status.RUNNING || downloader.getStatus() == AsyncTask.Status.PENDING)
           downloader.cancel(true);
      

      【讨论】:

        【解决方案7】:

        受问题启发,我编写了一个方法,该方法通过 AsyncTask 执行一些后台任务,如果处理时间超过 LOADING_TIMEOUT,则会出现一个重试的警报对话框。

        public void loadData()
            {
                final Load loadUserList=new Load();
                loadUserList.execute();
                Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (loadUserList.getStatus() == AsyncTask.Status.RUNNING) {
                            loadUserList.cancel(true);
                            pDialog.cancel();
                            new AlertDialog.Builder(UserList.this)
                                    .setTitle("Error..!")
                                    .setMessage("Sorry you dont have proper net connectivity..!\nCheck your internet settings or retry.")
                                    .setCancelable(false)
                                    .setPositiveButton("Retry", new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialogInterface, int i) {
                                            loadData();
                                        }
                                    })
                                    .setNegativeButton("Exit", new DialogInterface.OnClickListener() {
                                        @Override
        
        
                              public void onClick(DialogInterface dialogInterface, int i) {
                                        System.exit(0);
                                    }
                                })
                                .show();
                    }
                }
            }, LOADING_TIMEOUT);
            return;
        }
        

        【讨论】:

          猜你喜欢
          • 2016-10-30
          • 2013-02-08
          • 1970-01-01
          • 2016-07-14
          • 1970-01-01
          • 2018-07-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多