Android SDK AsyncTask doInBackground çalışmıyor (alt sınıf)


90

15/2/2012 itibariyle bunun neden işe yaramadığına dair iyi bir açıklama veya neden bulamadım. Çözüme en yakın olanı geleneksel Thread yaklaşımını kullanmaktır , ancak o zaman neden Android SDK'da çalışmayan (görünmeyen) bir sınıfı dahil edelim?

İyi akşamlar!

Bir AsyncTask alt sınıfım var:

// ParseListener had a callback which was called when an item was parsed in a
// RSS-xml, but as stated further down it is not used at all right now.
private class xmlAsync extends AsyncTask<String, RSSItem, Void> implements ParseListener

Bu şu şekilde idam edilir:

xmlAsync xmlThread = new xmlAsync();

xmlThread.execute("http://www.nothing.com");

Şimdi bu alt sınıf küçük bir hatayla karşılaştı. Daha önce biraz xml ayrıştırması yapıyordu , ancak doInBackground () olarak adlandırılmadığını fark ettiğimde onu satır satır soydum ve sonunda şununla bitirdim :

@Override
protected Void doInBackground(String... params) 
{
    Log.v(TAG, "doInBackground");
        return null;
}

Bazı nedenlerden dolayı hiçbir şey kaydetmedi. Ancak şunu ekledim:

@Override
protected void onPreExecute() 
{
        Log.v(TAG, "onPreExecute");
        super.onPreExecute();
}

Ve bu satır, iş parçacığı yürütülürken gerçekten günlüğe kaydedilir. Yani bir şekilde onPreExecute () çağrılır ama doInBackground () . Aynı zamanda arka planda çalışan ve gayet iyi çalışan başka bir AsyncTask var.

Şu anda uygulamayı Kuzey Kutbu'na yakın bir emülatörde, SDK Sürüm 15, Eclipse, Mac OS X 10.7.2'de çalıştırıyorum.

DÜZENLE:

@Override
    protected void onProgressUpdate(RSSItem... values) {

        if(values[0] == null)
        {
                            // activity function which merely creates a dialog
            showInputError();
        }
        else
        {

            Log.v(TAG, "adding "+values[0].toString());
            _tableManager.addRSSItem(values[0]);
        }


        super.onProgressUpdate(values);
    }

_tableManager.addRSSItem () az ya da çok, etkinliğin bağlamıyla başlatılan bir SQLiteDatabase'e bir satır ekler. publishProgress (), Arayüz ParseListener'ın geri araması tarafından çağrılır. Ancak, doInBackground () içinde log.v dışında hiçbir şey yapmadığım için, bunu ilk önce bile gereksiz buldum.

DÜZENLEME 2:

Pekala, tamamen açık olmak gerekirse, bu diğer AsyncTask, aynı aktivitede çalışıyor ve mükemmel çalışıyor.

private class dbAsync extends AsyncTask<Void, RSSItem, Void>
{
    Integer prevCount;
    boolean run;

    @Override
    protected void onPreExecute() {
        run = true;
        super.onPreExecute();
    }

    @Override
    protected Void doInBackground(Void... params) {
        // TODO Auto-generated method stub
        run = true;
        prevCount = 0;

        while(run)
        {
            ArrayList<RSSItem> items = _tableManager.getAllItems();

            if(items != null)
            {
                if(items.size() > prevCount)
                {
                    Log.v("db Thread", "Found new item(s)!");
                    prevCount = items.size();

                    RSSItem[] itemsArray = new RSSItem[items.size()];

                    publishProgress(items.toArray(itemsArray));
                }
            }               

            SystemClock.sleep(5000);
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(RSSItem... values) {

        ArrayList<RSSItem> list = new ArrayList<RSSItem>();

        for(int i = 0; i < values.length; i++)
        {
            list.add(i, values[i]);
        }

        setItemsAndUpdateList(list);

        super.onProgressUpdate(values);
    }

    @Override
    protected void onCancelled() {
        run = false;

        super.onCancelled();
    }
}

DÜZENLEME 3:

Ah, üzgünüm soru sormakta kötüyüm. Ama işte Görevlerin ilklendirilmesi.

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.execute("http://www.nothing.com", null);
}

görev tamamlanmadan faaliyetin bitmesi mümkün mü?
Paul Nikonowicz

Hiç alışılmadık bir şekilde. Bu durumda diğer iş parçacığım doğru çalışmaz mı? Ve şu anda sadece bir aktivitem var.
SeruK

2
Uygulamamda da aynı sorun var - doInBackground ya çağrılmıyor ya da çok uzun bir gecikmeyle çağrılıyor. İşte benim sınırlı gözlemim: tam olarak aynı kod bir Android 2.3.3 akıllı telefonda ve bir Android 2.3.3 emülatöründe kusursuz bir şekilde çalışıyor, ancak bu sorunu bir Android 4.0.3 tablet ve bir grup Android 4.xx emülatöründe var. Bu sorunun Android'in daha yeni sürümlerinde ortaya çıktığı sonucuna varmak çok cazip.
Hong

Maalesef bu sorunun yalnızca bir Aktivitenin ikinci AsyncTask'ında meydana geldiğini söylemeyi unuttum İlk AsyncTask her zaman iyi çalışıyor.
Hong

Hong, Matthieu'nun cevabını denedin mi? Çoğunlukla Android oyun ATM'sinden çıktım ve bir süredir onunla çalışmadım, bu yüzden cevaplarının gerçekten işe yarayıp yaramadığını anlayamıyorum. Senin için değilse, belki de cevabını kabul etmem kötüydü ...
SeruK

Yanıtlar:


107

Matthieu'nun çözümü çoğu için iyi çalışacaktır, ancak bazıları problemlerle karşılaşabilir; Anders Göransson'un açıklaması gibi burada veya web'den sağlanan birçok bağlantıda araştırma yapmadıkça . Burada diğer bazı okumaları özetlemeye ve executeOnExecutor hala tek iş parçacığında çalışıyorsa çözümü hızlı bir şekilde açıklamaya çalışıyorum ...

AsyncTask().execute();Android sürümlerine göre davranışı değişti. Önce Halka (Robot: 1.6 API: 4) görev gelen, seri olarak yürütülmüştür çörek için zencefilli (Android: 2.3 API: 9) görevler paralel çalıştırılacak; Honeycomb (Android: 3.0 API: 11) yürütme sıralı hale getirildiğinden beri ; AsyncTask().executeOnExecutor(Executor)ancak paralel yürütme için yeni bir yöntem eklendi.

Sıralı işlemede tüm Async görevler tek bir iş parçacığında çalışır ve bu nedenle önceki görev bitmeden önce beklemek zorundadır. Hemen kod çalıştırmanız gerekiyorsa, görevlerin ayrı iş parçacıkları içinde paralel olarak işlenmesi gerekir.

AsyncTask ile seri yürütme, Donut ve Honeycomb sürümleri arasında mevcut değilken, paralel yürütme Donut'tan önce mevcut değildir.

Donut'dan sonra paralel işleme için: Build sürümünü kontrol edin ve bu .execute () veya .executeOnExecutor () yöntemini kullanın. Aşağıdaki kod yardımcı olabilir ...

AsyncTask<Void,Void,Void> myTask = new AsyncTask<Void,Void,Void>() { ... }; // ... your AsyncTask code goes here
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
    myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
    myTask.execute();

NOTE:Fonksiyon .executeOnExecutor(), targetSdkVersionprojenin küçük veya ona eşit olup olmadığını kontrol eder HONEYCOMB_MR1(Android: 2.1 API: 7) ve ardından yürütücüyü olmaya zorlar THREAD_POOL_EXECUTOR(Honeycomb sonrası sırayla Görevleri çalıştırır).
Eğer bir tanımlamadıysanız targetSdkVersionsonra minSdkVersionotomatik olarak kabul edilir targetSdkVersion.
Bu nedenle, AsyncTask'ınızı Honeycomb sonrası paralel olarak çalıştırmak için targetSdkVersionboş bırakamazsınız .


1
Çok güzel cevap. Matthieu yanlış olmasa da, bir sürü önemli bilgi eklediğiniz için bunu kabul ediyorum.
SeruK

@Nashe Çok teşekkürler. Gerçekten çok yardımcı oluyor. Aynı sorunla 3 gündür mücadele ediyordum. Tekrar teşekkürler :)

Günümü kurtardım! Bu cevabın daha kolay bulunmasını dilerim.
zjk

4
Hey @Nashe, benim sorunum biraz garip. Bugünden önce AsyncTask üzerinde .execute () yöntemini kullanıyordum ve kod mükemmel çalışıyordu. Ama bugün sorunu anladım - denetim doInBackground () yöntemine girmiyor. Sağladığınız çözüm işe yarıyor olsa da, daha önce çözüm olmadan nasıl çalıştığına şaşırdım. Daha önce ve şimdi aynı Cihaz setini kullanıyorum.
Ravi Sisodia

Bu neden böyle olmalı? Neden beklentilere göre çalışmıyor? :(
Nikolay R

160

Bu yanıtı kontrol etmelisiniz: https://stackoverflow.com/a/10406894/347565 ve içerdiği google gruplarının bağlantısı.

Seninle benzer bir sorun yaşadım, neden çalışmadığı hala belirsiz, ancak kodumu bu şekilde değiştirdim ve sorun ortadan kalktı:

ASyncTask<Void,Void,Void> my_task = new ASyncTask<Void,Void,Void>() { ... };
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    my_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
else
    my_task.execute((Void[])null);

Bu gönderiye geri dönüp baktığımda çok kötüydüm; Uzun zamandır projeden uzaklaştım. İnsanlar işe yaradığını söylediği için bunu cevap olarak kabul edeceğim.
SeruK

bu benim için şimdi çalıştı ama keşke nedenini anlasaydım. Daha sonra durmadan önce execute ile iyi çalışıyordu. Yaptığım bir şey, aynı asynctask'ın onPostExecute içinde yeni bir asynctask başlatmaktır (yani onu yinelemeli olarak çağırıyorum) belki de bu soruna bağlı?
steveh

Bunun size ihtiyacınız olan tüm açıklamaları vermesi gerektiğini düşünüyorum: commonsware.com/blog/2012/04/20/…
Matthieu

1
@Matthieu: Efendim, size yeterince teşekkür edemem !!! Saatlerdir bu soruna kafa yoruyordum ve çözümünüz her şeyi bir cazibe gibi çalıştırdı! Harika cevap için çok teşekkür ederim. Keşke birden fazla oy verebilseydim !!
Swayam


9

Bunu iki şekilde yapabilirsiniz:

Yol 1 :

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) // Above Api Level 13
  {
      asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }
else // Below Api Level 13
  {
      asyncTask.execute();
  }

Durumunda yol 1 değil denemek için çalışır yolu 2 .

Yol 2 :

int mCorePoolSize = 60;
int mMaximumPoolSize = 80;
int mKeepAliveTime = 10;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(mMaximumPoolSize);
Executor mCustomThreadPoolExecutor = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.SECONDS, workQueue);
asyncTask.executeOnExecutor(mCustomThreadPoolExecutor);

Umarım bu sana yardımcı olur.


süper dostum çalışıyor ama AsyncTask.THREAD_POOL_EXECUTOR kullanan sonraki sonraki Async görevleri başarısız oluyor, bu yüzden tüm projede CustomExecutor ile değiştirmem gerekiyor sanırım :(
kumar

@vinu, AsyncTask yürütmek için ortak async görevi ve ortak yöntemi kullanmanızı öneririm. Umarım bu sana yardımcı olur.
Hiren Patel

2
Hey bu bana çok yardımcı oldu! Teşekkürler!
Justin Ebby

6

Ben de aynı sorunu yaşadım: Birincisinde "çalıştır" dedikten sonra ikinci bir AsyncTask yürütülemiyor: doInBackground yalnızca birincisi için çağrılıyor.

Bunun neden olduğunu cevaplamak için bu cevabı kontrol edin (SDK'ya bağlı olarak farklı davranış)

Bununla birlikte, sizin durumunuz için, bu engel executeOnExecutor kullanılarak önlenebilir (3.0'dan itibaren benim için 4.0.3 ile çalıştı) ancak iş parçacığı havuzu boyutu ve kuyruğa ilişkin sınırlamalara dikkat edin.

Bunun gibi bir şey deneyebilir misin:

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.executeOnExecutor(_dbLookup.THREAD_POOL_EXECUTOR
 ,"http://www.nothing.com", null);
}

Güncelleme sorunuz için: Temel olarak, intereference gibi çoklu okumadan kaynaklanabilecek tüm sorunları önlemek için belgelerde açıklanmıştır ...


5

Bilmek istediğim ve aslında sorununuzu çözebilecek bir şey, sınıfınızın örneğini nerede başlatıyorsunuz ve execute () yöntemini çağırıyorsunuz? AsyncTask belgelerini okursanız, bu işlemlerin her ikisinin de ana UI iş parçacığında gerçekleştirilmesi gerekir. Nesnenizi oluşturuyor ve başka bir iş parçacığından execute çağırıyorsanız, onPreExecute çalışabilir, burada% 100 emin değilim, ancak arka plan iş parçacığı oluşturulmayacak ve çalıştırılmayacak.

AsyncTask'ınızın örneğini bir arka plan iş parçacığından veya ana UI iş parçacığında gerçekleşmeyen başka bir işlem oluşturuyorsanız, şu yöntemi kullanmayı düşünebilirsiniz: Activity.runOnUiThread (Runnable)

Bu yöntemi çağırmak için çalışan Activity'inizin bir örneğine erişmeniz gerekir, ancak bu, UI iş parçacığında çalışmayan başka bir koddan UI iş parçacığında kod çalıştırmanıza izin verir.

Umarım bu mantıklıdır. Daha fazla yardımcı olabilirsem haberim olsun.

David


cevabınıza eklediğinizde, bu ileti dizisinin ilginç bir cevabı var stackoverflow.com/questions/4080808/… .
manjusg

Harika cevap için teşekkürler! Bunu yükselttim çünkü bunun yaygın bir AsyncTask başlangıç ​​sorunu olabileceğini düşünüyorum. Ne yazık ki, bu konu için tam olarak doğru cevap değil. Her iki sınıf da ana UI iş parçacığı üzerinde çalışan bir etkinliğin onCreate () öğesinde somutlaştırılır. Bu projede sadece bir faaliyetim var.
SeruK

@manjusg Başından beri AsyncTask'ın kararsız olmasıyla bir ilgisi olduğunu düşündüm, belki de birkaç tanesi aynı anda çalıştırıldığında çok fazla. Öyleyse neden?
SeruK

SO'nun arka arkaya üç kez hızlı bir şekilde yayınlamak için hangi politikalara sahip olduğunu gerçekten bilmiyorum, ancak bunu diğer başlıkta buldum ... foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html "AsyncTask kullanıyor Sabit kodlanmış 10 öğe sınırına sahip statik bir dahili iş kuyruğu. " Bu bir şeyi kanıtlayabilir, ancak benim sadece iki AsyncTask-alt sınıflarım var! Normal diş açma yöntemlerini kullanmaktan gerçekten kaçınmak istiyorum çünkü sonunda dere'de çok fazla ayrıştırma yapılacak.
SeruK

2

Android Acımasızdır! Buna inanamıyorum, günden bugüne değişen flakey uygulaması. Bir gün tek bir iş parçacığı, sonraki 5 diğeri 128.

Her neyse, burada stok AsyncTask'ın yerine neredeyse bir düşüş var. İsterseniz buna AsyncTask bile diyebilirsiniz, ancak karışıklığı önlemek için adı ThreadedAsyncTask. Execute () nihai olduğu için execute yerine executeStart () çağırmanız gerekir.

/**
 * @author Kevin Kowalewski
 *
 */
public abstract class ThreadedAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { 
    public AsyncTask<Params, Progress, Result> executeStart(Params... params){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            return executePostHoneycomb(params);
        }else{
            return super.execute(params);
        }
    }


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private AsyncTask<Params, Progress, Result> executePostHoneycomb(Params... params){
        return super.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 
    }
}

Bekle, Android'in bazı sürümlerinin eşzamansız işlemleri tek bir iş parçacığıyla sınırladığını söylemiyorsun, değil mi? Bu inanılmaz derecede aptalca olurdu. (Bir süredir Android oyunundan
çıktım

Evet, Android 3.0 ve sonraki sürümlerde AsyncTask.THREAD_POOL_EXECUTOR'ı kullanmazsanız, yalnızca bir iş parçacığı havuzu alırsınız. Kendiniz için deneyin, iki AsyncTask yapın ve doInBackground'da uyuyun. Android AsyncTask belgelerinden: "HONEYCOMB ile başlayarak, paralel yürütmeden kaynaklanan yaygın uygulama hatalarını önlemek için görevler tek bir iş parçacığı üzerinde yürütülür."
Kevin Parker

1

Bunun iş parçacığı için gerçekten geç olabileceğini biliyorum, ancak daha sonraki android emülatörlerinde çalışmamasının bir nedeni var. Asynctask piyasaya sürüldüğünde, android sadece bir seferde bir tane çalıştırmanıza izin verir, sonra bir süre sonra, hangi sürümden emin değilim, aynı anda birden fazla eşzamansız görev çalıştırmanıza izin verdiler, bu birçok uygulamada sorunlara neden oldu ve Honeycomb + 'da yalnızca geri döndüler tek seferde bir eşzamansız görevin çalışmasına izin verir. İş parçacığı havuzunu manuel olarak değiştirmediğiniz sürece. Umarım bu insanlar için bir veya iki şeyi çözer.


0

sdk olduğunu düşünüyorum. aynı sorunu yaşadım ve hedef sdk'yi 15'ten 11'e değiştirdikten sonra her şey mükemmel çalışıyor.

sdk15 ile, AsyncTask.Status ÇALIŞIYOR olsa bile, doInBackground asla çağrılmaz. ui iş parçacığı ile bir ilgisi olduğunu düşünüyorum.


Şu anda test etmek için gerçekten zamanım olmadığı için ne inkar edebilirim ne de teyit edebilirim. Söyleyebileceğim tek şey, SDK 15 kullandığım, bu yüzden büyük olasılıkla.
SeruK

0

Matthieu'nun cevabına göre, uygulamanızda yinelenen kodlardan kaçınmak için SDK sürümüne bağlı olarak doğru şekilde yürütmek için yardımcı sınıfın altında AsyncTask:

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.os.Build;

public class AsyncTaskExecutor<Params, Progress, Result> {

  @SuppressLint("NewApi")
  public AsyncTask<Params, Progress, Result> execute(final AsyncTask<Params, Progress, Result> asyncTask, final Params... params){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
      return asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }  else{
      return asyncTask.execute(params);
    }
  }

}

Kullanım örneği:

public class MyTask extends AsyncTask<Void, Void, List<String>> {

...

final MyTask myTask = new MyTask();
new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);
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.