Jalankan AsyncTask beberapa kali

127

Dalam Aktivitas saya, saya menggunakan kelas yang memanjang dari AsyncTask dan parameter yang merupakan instance dari AsyncTask itu. Ketika saya menelepon mInstanceOfAT.execute("")semuanya baik-baik saja. Tetapi aplikasi mogok ketika saya menekan tombol pembaruan yang memanggil lagi AsyncTask (Jika pekerjaan jaringan tidak bekerja). Karena kemudian muncul Pengecualian yang mengatakan

Tidak dapat menjalankan tugas: tugas sudah dijalankan (tugas hanya dapat dilakukan sekali)

Saya sudah mencoba menelepon cancel (true) untuk instance Asyctask, tetapi tidak berhasil juga. Satu-satunya solusi sejauh ini adalah membuat instance baru dari Asyntask. Apakah itu cara yang benar?

Terima kasih.

Dayerman
sumber

Jawaban:

217

AsyncTask contoh hanya dapat digunakan satu kali.

Sebaliknya, panggil saja tugas Anda seperti new MyAsyncTask().execute("");

Dari dokumen AsyncTask API:

Aturan Threading

Ada beberapa aturan threading yang harus diikuti agar kelas ini berfungsi dengan baik:

  • Contoh tugas harus dibuat di utas UI.
  • mengeksekusi (Params ...) harus dipanggil pada utas UI.
  • Jangan panggil onPreExecute (), onPostExecute (Hasil), doInBackground (Params ...), onProgressUpdate (Progress ...) secara manual.
  • Tugas hanya dapat dieksekusi sekali (pengecualian akan dilemparkan jika eksekusi kedua dicoba.)
Steve Prentice
sumber
2
Bahwa apa yang saya katakan telah saya lakukan, apakah itu satu-satunya kemungkinan? Karena saya ingin menyimpan memmory, daripada membuat objek baru.
Dayerman
1
Lihat juga stackoverflow.com/questions/2711183/…
Steve Prentice
@StevePrentice: jika saya membuat turunan tugas setiap x detik dengan tugas baru () .eksekusi (param), untuk mengirim data ke server, bagaimana pemulung dapat melepaskan memori ketika eksekusi selesai?
Ant4res
3
@ Ant4res, Selama Anda tidak mereferensikan instance tugas async, GC akan melepaskan memori. Namun, jika Anda memiliki tugas latar belakang yang sedang berlangsung, Anda dapat mempertimbangkan untuk melakukannya dalam satu lingkaran di dalam doInBackground dan membuat panggilan untuk menerbitkan Kemajuan untuk memperbarui kemajuan. Atau, pendekatan lain adalah menempatkan tugas Anda di utas latar belakang. Banyak pendekatan berbeda di sini, tetapi tidak dapat merekomendasikan satu sama lain tanpa detail.
Steve Prentice
28

Alasan untuk contoh ASyncTask yang mudah-dan-terinci dijelaskan dengan sangat baik dalam jawaban Steve Prentice - Namun, sementara Anda dibatasi pada berapa kali Anda menjalankan ASyncTask, Anda bebas untuk melakukan apa yang Anda suka saat utas berjalan. .

Masukkan kode yang dapat dieksekusi di dalam loop di dalam doInBackground () dan gunakan kunci bersamaan untuk memicu setiap eksekusi. Anda dapat mengambil hasilnya menggunakan publishProgress () / onProgressUpdate () .

Contoh:

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

Tentu saja, ini sedikit lebih rumit daripada penggunaan tradisional ASyncTask, dan Anda berhenti menggunakan publishProgress () untuk pelaporan kemajuan aktual. Tetapi jika memori menjadi perhatian Anda, maka pendekatan ini akan memastikan hanya satu ASyncTask yang tersisa di heap saat runtime.

seanhodges
sumber
Tetapi intinya adalah bahwa saya tidak ingin mengeksekusi kembali Asyntask saat sedang berjalan, tetapi karena ini sudah selesai dan belum menerima data sebagaimana mestinya, maka panggil lagi.
Dayerman
Sebenarnya Anda hanya menjalankan ASyncTask sekali dengan cara ini, dan Anda dapat memeriksa apakah data sudah benar di dalam metode onPublishProgress (atau mendelegasikan cek di tempat lain). Saya menggunakan pola ini untuk masalah yang sama beberapa waktu lalu (banyak tugas yang dipecat secara berurutan, mempertaruhkan ukuran tumpukan).
seanhodges
Tetapi bagaimana jika pada saat itu server tidak merespons, dan saya ingin mencoba lagi 10 detik kemudian? AsyncTask sudah selesai, rigth? Maka saya perlu menyebutnya lagi
Dayerman
Saya telah menambahkan beberapa kode contoh untuk menjelaskan apa yang saya maksud. ASyncTask hanya akan selesai setelah Anda puas dengan hasilnya dan memanggil "terminateTask ()".
seanhodges
1
Jika Anda mendapatkan IllegalMonitorStateExceptiondalam runAgain(disebut oleh onProgressUpdate) melihat jawaban ini: stackoverflow.com/a/42646476/2711811 . Ini menunjukkan (dan bekerja untuk saya) bahwa signal()kebutuhan untuk dikelilingi oleh lock/ unlock. Ini mungkin ada hubungannya dengan waktu publishProgresspanggilan onProgressUpdate.
Andy
2

Saya memiliki masalah yang sama. Dalam kasus saya, saya punya tugas yang ingin saya lakukan di dalam onCreate()dan di onResume(). Jadi saya membuat Asynctask saya statis, dan dapatkan instance darinya. Sekarang kami masih memiliki masalah yang sama.

Jadi yang saya lakukan di onPostExecute () adalah ini:

instance = null;

Perlu diingat bahwa saya memeriksa metode getInstance statis bahwa instance saya tidak nol, kalau tidak saya membuatnya:

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

Metode di postExecute akan mengosongkan instance dan membuatnya kembali. Tentu ini bisa dilakukan di luar kelas.

SamuelD
sumber
1

Saya telah membuat tugas rotasi saya statis, yang kemudian membantu saya melampirkan, melepaskan, dan memasang kembali ke utas UI pada perubahan rotasi. Tetapi untuk kembali ke pertanyaan Anda, apa yang saya lakukan adalah membuat bendera untuk melihat apakah utas berjalan. Ketika Anda ingin memulai kembali utas, saya memeriksa apakah tugas rotasi berjalan jika saya memanggang peringatan. Jika tidak, saya membuatnya nol dan kemudian membuat yang baru, yang akan mengatasi kesalahan yang Anda lihat. Selanjutnya, setelah berhasil menyelesaikan saya membatalkan tugas sadar rotasi selesai sehingga siap untuk pergi lagi.

Arnab C.
sumber
0

Ya itu benar, dokter mengatakan bahwa hanya dapat dieksekusi satu Asyntask.

Setiap kali Anda perlu menggunakannya, Anda harus misalnya:

// 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> {
}
Victor Ruiz.
sumber