AsyncTask'ı birkaç kez yürütün


127

Aktivitemde, AsyncTask'tan uzanan bir sınıf ve bu AsyncTask'ın bir örneği olan bir parametre kullanıyorum. Aradığımda mInstanceOfAT.execute("")her şey yolunda. Ancak, AsyncTask'ı tekrar çağıran bir güncelleme düğmesine bastığımda uygulama çöküyor (Ağ işi işe yaramadıysa). Neden daha sonra bir İstisna belirir

Görev yürütülemiyor: görev zaten yürütülmüştür (bir görev yalnızca bir kez yürütülebilir)

Asyctask örneği için cancel (true) 'yu çağırmayı denedim, ancak bu da çalışmıyor. Şimdiye kadarki tek çözüm, Asyntask'ın yeni örneklerini oluşturmaktır. Bu doğru yol mu?

Teşekkürler.

Yanıtlar:


217

AsyncTask örnekler yalnızca bir kez kullanılabilir.

Bunun yerine, görevinizi şöyle çağırın: new MyAsyncTask().execute("");

AsyncTask API belgelerinden:

Diş çekme kuralları

Bu sınıfın düzgün çalışması için uyulması gereken birkaç iş parçacığı kuralı vardır:

  • Görev örneği, UI iş parçacığında oluşturulmalıdır.
  • execute (Params ...) UI iş parçacığında çağrılmalıdır.
  • OnPreExecute (), onPostExecute (Sonuç), doInBackground (Params ...), onProgressUpdate (Progress ...) 'i manuel olarak çağırmayın.
  • Görev yalnızca bir kez yürütülebilir (ikinci bir yürütme denenirse bir istisna atılır.)

2
Yaptığımı söylediğim tek olasılık bu mu? Çünkü yeni bir nesne oluşturmak yerine hafızayı kaydetmek istiyorum.
Dayerman


@StevePrentice: Yeni görev () ile her x saniyede bir görevin bir örneğini oluşturursam, bir sunucuya veri göndermek için çalıştır (param), çöp toplayıcı çalıştırma tamamlandığında belleği nasıl serbest bırakabilir?
Ant4res

3
@ Ant4res, Eşzamansız görev örneğine başvurmadığınız sürece, GC belleği serbest bırakacaktır. Ancak, devam eden bir arka plan göreviniz varsa, bunu doInBackground içinde bir döngü içinde yapmayı düşünebilir ve ilerlemeyi güncellemek için publishProgress çağrıları yapabilirsiniz. Veya başka bir yaklaşım, görevinizi bir arka plan dizisine koymak olabilir. Burada birçok farklı yaklaşım var, ancak ayrıntılar olmadan birbirini tavsiye edemezsiniz.
Steve Prentice

28

ASyncTask'ın ateşle ve unut örneklerinin nedenleri Steve Prentice'nin cevabında oldukça iyi detaylandırılmıştır - Bununla birlikte, bir ASyncTask'ı kaç kez çalıştırdığınız konusunda kısıtlıyken, iş parçacığı çalışırken istediğinizi yapmakta özgürsünüz .. .

Yürütülebilir kodunuzu doInBackground () içindeki bir döngünün içine koyun ve her yürütmeyi tetiklemek için eşzamanlı bir kilit kullanın. PublishProgress () / onProgressUpdate () öğesini kullanarak sonuçları alabilirsiniz .

Misal:

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

Elbette, bu ASyncTask'ın geleneksel kullanımından biraz daha karmaşıktır ve gerçek ilerleme raporlaması için publishProgress () kullanımından vazgeçersiniz . Ancak endişeniz bellekse, bu yaklaşım çalışma zamanında yığında yalnızca bir ASyncTask kalmasını sağlayacaktır.


Ama asıl mesele şu ki, Asyntask'i çalışırken yeniden çalıştırmak istemiyorum, ancak bu işlemin bitmesi ve veriyi olması gerektiği gibi almaması, sonra tekrar çağırmak.
Dayerman

Aslında, ASyncTask'ı bu şekilde yalnızca bir kez yürütürsünüz ve verilerin onPublishProgress yönteminde doğru olup olmadığını kontrol edebilirsiniz (veya kontrolü başka bir yerde yetkilendirebilirsiniz). Bu kalıbı bir süre önce benzer bir sorun için kullandım (yığın boyutunu riske atan birçok görev hızlı bir şekilde arka arkaya çalıştırılıyor).
seanhodges

Peki ya o anda sunucu yanıt vermezse ve 10 saniye sonra tekrar denemek istersem ne olur? AsyncTask zaten bitti, tamam mı? O halde tekrar aramam gerekiyor
Dayerman

Ne demek istediğimi açıklamak için bazı örnek kodlar ekledim. ASyncTask yalnızca sonuçtan memnun olduğunuzda ve "terminateTask ()" çağrısı yaptığınızda biter.
seanhodges

1
Eğer alırsanız IllegalMonitorStateExceptioniçinde runAgain(tarafından çağrılan onProgressUpdate: Bu yanıtı bakınız) stackoverflow.com/a/42646476/2711811 . signal()İhtiyaçların a lock/ ile çevrelenmesi gerektiğini öneriyor (ve benim için çalıştı) unlock. Bu, publishProgressaramanın zamanlamasıyla ilgili olabilir onProgressUpdate.
Andy

2

Ben de aynı sorunu yaşadım. Benim durumumda ben yapmak istediğim bir görev var onCreate()ve içinde onResume(). Bu yüzden Asynctaskımı statik yaptım ve ondan örneği aldım. Şimdi hala aynı problemimiz var.

Yani onPostExecute () içinde yaptığım şey şuydu:

instance = null;

Statik getInstance yönteminde örneğimin boş olmadığını kontrol ettiğimi, yoksa onu oluşturduğumu unutmayın:

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

PostExecute içindeki yöntem örneği boşaltacak ve yeniden oluşturacaktır. Elbette bu ders dışında da yapılabilir.


1

Döndürme görevlerimi statik hale getirdim, bu da onları döndürme değişikliklerinde UI iş parçacıklarına eklememe, ayırmama ve yeniden eklememe yardımcı oldu. Ancak sorunuza geri dönersek, yaptığım şey iş parçacığının çalışıp çalışmadığını görmek için bir bayrak oluşturmak. İş parçacığını yeniden başlatmak istediğinizde, döndürme görevinin çalışıp çalışmadığını kontrol ederim, eğer bir uyarı tost yapıyorum. Değilse, onu boş hale getiririm ve sonra yeni bir tane oluştururum, bu gördüğünüz hatanın etrafında çalışacaktır. Ayrıca, başarılı bir şekilde tamamlandıktan sonra, tamamlanmış dönüş farkındalığı görevini geçersiz kılıyorum, böylece yeniden başlamaya hazır olur.


0

Evet doğru, doktor sadece bir Asyntask'ın yürütülebileceğini söylüyor.

Her kullanmanız gerektiğinde, örneklemeniz gerekir:

// 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> {
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.