Android - Đặt thời gian chờ cho AsyncTask?


125

Tôi có một AsyncTasklớp mà tôi thực hiện tải xuống một danh sách dữ liệu lớn từ một trang web.

Trong trường hợp người dùng cuối có kết nối dữ liệu rất chậm hoặc không chính xác tại thời điểm sử dụng, tôi muốn thực hiện AsyncTaskhết thời gian chờ sau một khoảng thời gian. Cách tiếp cận đầu tiên của tôi về điều này là như vậy:

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 );

Sau khi bắt đầu AsyncTask, một trình xử lý mới được bắt đầu sẽ hủy AsyncTasksau 30 giây nếu nó vẫn chạy.

Đây có phải là một cách tiếp cận tốt? Hoặc có một cái gì đó được xây dựng vào AsyncTaskđó là phù hợp hơn cho mục đích này?


18
Sau khi thử một vài cách tiếp cận khác nhau, tôi kết luận rằng câu hỏi của bạn nên là câu trả lời được chấp nhận.
JohnEye

Cảm ơn câu hỏi tôi thực sự rơi vào cùng một vấn đề và đoạn mã của bạn đã giúp tôi +1
Ahmad Dwaik 'Warlock'

1
Câu hỏi của bạn tự nó là một câu trả lời hay +50 :)
karthik kolanji

Câu trả lời:


44

Có, có AsyncTask.get ()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

Lưu ý rằng bằng cách gọi điều này trong luồng chính (AKA. Giao diện người dùng) sẽ chặn thực thi, Bạn có thể cần gọi nó trong một luồng riêng.


9
Có vẻ như nó đánh bại mục đích sử dụng AsyncTask để bắt đầu nếu phương thức hết thời gian này chạy trên luồng UI chính ... Tại sao không sử dụng handlercách tiếp cận mà tôi mô tả trong câu hỏi của mình? Có vẻ đơn giản hơn nhiều vì luồng UI không bị đóng băng trong khi chờ chạy Runlerable của Handler ...
Jake Wilson

Được rồi cảm ơn, tôi chỉ không chắc có cách nào phù hợp để làm điều đó hay không.
Jake Wilson

3
Tôi không hiểu tại sao bạn gọi get () trong một chủ đề khác. Có vẻ lạ vì bản thân AsyncTask là một loại chủ đề. Tôi nghĩ giải pháp của Jakobud ổn hơn.
emeraldhieu

Tôi nghĩ .get () tương tự như tham gia của chủ đề posix, trong đó mục đích là chờ đợi chủ đề hoàn thành. Trong trường hợp của bạn, nó phục vụ như là một thời gian chờ, đó là một tài sản hoàn cảnh. Đó là lý do tại sao bạn cần gọi .get () từ trình xử lý hoặc từ một luồng khác để tránh chặn UI chính
Constantine Samoilenko

2
Tôi biết điều này là nhiều năm sau, nhưng điều này vẫn chưa được tích hợp vào Android nên tôi đã tạo một lớp hỗ trợ. Tôi hy vọng nó sẽ giúp được ai đó. gist.github.com/scottTomaszewski/ từ
Scott Tomaszewski

20

Trong trường hợp, trình tải xuống của bạn dựa trên kết nối URL, bạn có một số tham số có thể giúp bạn xác định thời gian chờ mà không cần mã phức tạp:

  HttpURLConnection urlc = (HttpURLConnection) url.openConnection();

  urlc.setConnectTimeout(15000);

  urlc.setReadTimeout(15000);

Nếu bạn chỉ cần đưa mã này vào tác vụ async của mình thì không sao.

'Đọc hết thời gian chờ' là để kiểm tra một mạng xấu trong quá trình chuyển.

'Hết thời gian kết nối' chỉ được gọi khi bắt đầu để kiểm tra xem máy chủ có hoạt động hay không.


1
Tôi đồng ý. Sử dụng phương pháp này đơn giản hơn và nó thực sự chấm dứt kết nối khi hết thời gian chờ. Sử dụng phương pháp AsyncTask.get () bạn chỉ được thông báo rằng đã đạt đến giới hạn thời gian, nhưng quá trình tải xuống vẫn đang được xử lý và thực sự có thể hoàn tất sau đó - gây ra nhiều sự phức tạp hơn trong mã. Cảm ơn.
choáng váng

Xin lưu ý rằng điều này là không đủ khi sử dụng nhiều luồng trên các kết nối internet rất chậm. Thêm thông tin: rò rỉfromjavaheap.blogspot.nl / 2013/12 / trênthushw.blogspot.nl/2010/10/ trên
Kapitein Witbaard

20

Sử dụng Lớp CountDownTimer bên cạnh lớp mở rộng cho AsyncTask trong phương thức onPreExecute ():

Ưu điểm chính, việc giám sát Async được thực hiện trong nội bộ lớp.

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();
...

thay đổi CountDownTimer (7000, 7000) -> CountDownTimer (7000, 1000) chẳng hạn và nó sẽ gọi onTick () 6 lần trước khi gọi onFinish (). Điều này là tốt nếu bạn muốn thêm một số giám sát.

Cảm ơn tất cả những lời khuyên tốt mà tôi đã nhận được trong trang này :-)


2

Tôi không nghĩ có bất cứ thứ gì giống như được tích hợp trong AsyncTask. Cách tiếp cận của bạn có vẻ là một trong những tốt. Chỉ cần đảm bảo kiểm tra định kỳ giá trị của isCancelling () trong phương thức doInBackground của AsyncTask để kết thúc phương thức này khi luồng UI hủy bỏ nó.

Nếu bạn muốn tránh sử dụng trình xử lý vì một số lý do, bạn có thể kiểm tra System.cienTimeMillis định kỳ trong AsyncTask và thoát khi hết thời gian chờ, mặc dù tôi thích giải pháp của bạn hơn vì nó thực sự có thể làm gián đoạn chuỗi.


0
         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();

         }

2
Cũng xem xét thêm một số lời giải thích
Saif Asif

0

Bạn có thể đặt thêm một điều kiện để hủy bỏ mạnh mẽ hơn. ví dụ,

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

0

Lấy cảm hứng từ câu hỏi Tôi đã viết một phương thức thực hiện một số tác vụ nền thông qua AsyncTask và nếu quá trình xử lý mất nhiều hơn thì LOADING_TIMEOUT thì một đoạn hội thoại cảnh báo để thử lại sẽ xuất hiện.

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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.