Thực thi AsyncTask nhiều lần


127

Trong Hoạt động của tôi, tôi sử dụng một lớp mở rộng từ AsyncTask và một tham số là một thể hiện của AsyncTask. Khi tôi gọi mInstanceOfAT.execute("")mọi thứ đều ổn. Nhưng ứng dụng gặp sự cố khi tôi nhấn nút cập nhật sẽ gọi lại AsyncTask (Trong trường hợp công việc mạng không hoạt động). Nguyên nhân sau đó xuất hiện một Ngoại lệ cho biết

Không thể thực thi tác vụ: tác vụ đã được thực thi (một tác vụ chỉ có thể được thực thi một lần)

Tôi đã thử gọi hủy (đúng) cho ví dụ của Asyctask, nhưng nó cũng không hoạt động. Giải pháp duy nhất cho đến nay là tạo ra các phiên bản mới của Asyntask. Đó có phải là cách chính xác?

Cảm ơn.

Câu trả lời:


217

AsyncTask trường hợp chỉ có thể được sử dụng một lần.

Thay vào đó, chỉ cần gọi nhiệm vụ của bạn như new MyAsyncTask().execute("");

Từ các tài liệu API AsyncTask:

Quy tắc luồng

Có một vài quy tắc luồng phải được tuân theo để lớp này hoạt động đúng:

  • Ví dụ tác vụ phải được tạo trên luồng UI.
  • thực thi (Params ...) phải được gọi trên luồng UI.
  • Không gọi onPreExecute (), onPostExecute (Kết quả), doInBackground (Params ...), onProTHERUpdate (Tiến trình ...) theo cách thủ công.
  • Tác vụ chỉ có thể được thực thi một lần (một ngoại lệ sẽ được ném nếu thực hiện lần thứ hai.)

2
Đó là những gì tôi nói tôi đã làm, đó có phải là khả năng duy nhất? Vì tôi muốn lưu memmory, thay vì tạo một đối tượng mới.
Dayerman


....
Ant4res

3
@ Ant4res, Miễn là bạn không tham chiếu thể hiện tác vụ không đồng bộ, thì GC sẽ giải phóng bộ nhớ. Tuy nhiên, nếu bạn có một tác vụ nền đang diễn ra, bạn có thể xem xét thực hiện nó trong một vòng lặp bên trong doInBackground và thực hiện các cuộc gọi đến PublishProTHER để cập nhật tiến trình. Hoặc, một cách tiếp cận khác sẽ là đưa nhiệm vụ của bạn vào một luồng nền. Rất nhiều cách tiếp cận khác nhau ở đây, nhưng không thể đề xuất cái này qua cái khác mà không có chi tiết.
Steve Prentice

28

Các lý do cho các trường hợp cháy và quên của ASyncTask được nêu chi tiết khá rõ trong câu trả lời của Steve Prentice - Tuy nhiên, trong khi bạn bị hạn chế số lần bạn thực thi ASyncTask, bạn có thể tự do làm những gì bạn thích trong khi luồng đang chạy .. .

Đặt mã thực thi của bạn bên trong một vòng lặp trong doInBackground () và sử dụng khóa đồng thời để kích hoạt mỗi lần thực thi. Bạn có thể truy xuất kết quả bằng cách sử dụng PublishProTHER () / onProTHERUpdate () .

Thí dụ:

class GetDataFromServerTask extends AsyncTask<Input, Result, Void> {

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition tryAgain = lock.newCondition();
    private volatile boolean finished = false;

    @Override
    protected Void doInBackground(Input... params) {

        lock.lockInterruptibly();

        do { 
            // This is the bulk of our task, request the data, and put in "result"
            Result result = ....

            // Return it to the activity thread using publishProgress()
            publishProgress(result);

            // At the end, we acquire a lock that will delay
            // the next execution until runAgain() is called..
            tryAgain.await();

        } while(!finished);

        lock.unlock();
    }

    @Override
    protected void onProgressUpdate(Result... result) 
    {
        // Treat this like onPostExecute(), do something with result

        // This is an example...
        if (result != whatWeWant && userWantsToTryAgain()) {
            runAgain();
        }
    }

    public void runAgain() {
        // Call this to request data from the server again
        tryAgain.signal();
    }

    public void terminateTask() {
        // The task will only finish when we call this method
        finished = true;
        lock.unlock();
    }

    @Override
    protected void onCancelled() {
        // Make sure we clean up if the task is killed
        terminateTask();
    }
}

Tất nhiên, điều này phức tạp hơn một chút so với cách sử dụng ASyncTask truyền thống và bạn từ bỏ việc sử dụng PublishProTHER () để báo cáo tiến độ thực tế. Nhưng nếu bộ nhớ là mối quan tâm của bạn, thì phương pháp này sẽ đảm bảo chỉ còn một ASyncTask trong heap khi chạy.


Nhưng vấn đề là tôi không muốn thực hiện lại Asyntask trong khi đang chạy, nhưng vì lý do này đã kết thúc và không nhận được dữ liệu như mong muốn, sau đó gọi lại.
Dayerman

Trên thực tế, bạn chỉ thực hiện ASyncTask một lần theo cách này và bạn có thể kiểm tra xem dữ liệu có đúng trong phương thức onPublishProTHER không (hoặc ủy quyền kiểm tra ở một nơi khác). Tôi đã sử dụng mô hình này cho một vấn đề tương tự một thời gian trước (rất nhiều nhiệm vụ được thực hiện liên tiếp, có nguy cơ kích thước đống).
seanhodges

Nhưng nếu trong thời điểm đó, máy chủ không phản hồi và tôi muốn thử lại 10 giây sau thì sao? AsyncTask đã kết thúc rồi, tôi có cần gọi lại không
Dayerman

Tôi đã thêm một số mã ví dụ để mô tả những gì tôi muốn nói. ASyncTask sẽ chỉ kết thúc khi bạn hài lòng với kết quả và gọi "terminatingTask ()".
seanhodges

1
Nếu bạn nhận được IllegalMonitorStateExceptiontrong runAgain(gọi bằng onProgressUpdate) xem câu trả lời này: stackoverflow.com/a/42646476/2711811 . Nó gợi ý (và làm việc cho tôi) rằng các signal()nhu cầu được bao quanh bởi lock/ unlock. Điều này có thể phải làm với thời gian của publishProgresscuộc gọi onProgressUpdate.
Andy

2

Tôi gặp vấn đề tương tự. Trong trường hợp của tôi, tôi có một nhiệm vụ tôi muốn làm trong onCreate()và trong onResume(). Vì vậy, tôi đã làm cho Asynctask của mình tĩnh và lấy ví dụ từ nó. Bây giờ chúng ta vẫn có cùng một vấn đề.

Vì vậy, những gì tôi đã làm trong onPostExecute () là đây:

instance = null;

Hãy nhớ rằng tôi kiểm tra phương thức getInstance tĩnh mà cá thể của tôi không phải là null, nếu không tôi sẽ tạo nó:

if (instance == null){
    instance = new Task();
}
return instance;

Phương thức trong postExecute sẽ làm trống cá thể và tạo lại nó. Tất nhiên điều này có thể được thực hiện bên ngoài lớp học.


1

Tôi đã làm cho các tác vụ xoay vòng của mình ở trạng thái tĩnh, sau đó giúp tôi đính kèm, tách và gắn lại chúng vào các luồng UI khi thay đổi xoay vòng. Nhưng để trở lại câu hỏi của bạn, những gì tôi làm là tạo một cờ để xem nếu luồng đang chạy. Khi bạn muốn khởi động lại chuỗi, tôi kiểm tra xem tác vụ xoay có đang chạy hay không nếu đó là tôi sẽ cảnh báo. Nếu không, tôi làm cho nó rỗng và sau đó tạo một cái mới, nó sẽ khắc phục lỗi bạn đang thấy. Hơn nữa, khi hoàn thành thành công, tôi loại bỏ nhiệm vụ nhận biết xoay vòng đã hoàn thành để nó sẵn sàng hoạt động trở lại.


0

Đúng vậy, tài liệu nói rằng chỉ có thể được thực thi một Asyntask.

Mỗi khi bạn cần sử dụng nó, bạn phải ví dụ:

// Any time if you need to call her
final FirmwareDownload fDownload = new FirmwareDownload();
fDownload.execute("your parameter");

static class FirmwareDownload extends AsyncTask<String, String, String> {
}
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.