Android SDK AsyncTask doInBackground läuft nicht (Unterklasse)

90

Bis zum 15.2.2012 habe ich noch keine gute Erklärung und keinen Grund gefunden, warum dies nicht funktioniert. Am nächsten an einer Lösung ist die Verwendung des traditionellen Thread- Ansatzes. Warum sollte dann eine Klasse in das Android SDK aufgenommen werden, die (anscheinend) nicht funktioniert?

Evenin 'SO!

Ich habe eine AsyncTask-Unterklasse:

// 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

Das wird so ausgeführt:

xmlAsync xmlThread = new xmlAsync();

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

Jetzt ist diese Unterklasse auf einen kleinen Fehler gestoßen. Früher wurde XML-Analyse durchgeführt, aber als ich bemerkte, dass doInBackground () nicht aufgerufen wurde, habe ich es Zeile für Zeile entfernt und schließlich genau das erreicht:

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

Was aus irgendeinem Grund nichts protokollierte. Ich habe jedoch Folgendes hinzugefügt:

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

Und diese Zeile wird tatsächlich beim Ausführen des Threads protokolliert. Also wird onPreExecute () irgendwie aufgerufen, aber nicht doInBackground () . Ich habe eine andere AsyncTask, die gleichzeitig im Hintergrund läuft, was gut funktioniert.

Ich verwende die App derzeit auf einem Emulator, SDK Version 15, Eclipse, Mac OS X 10.7.2, in der Nähe des Nordpols.

BEARBEITEN:

@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 () fügt einer SQLiteDatabase mehr oder weniger eine Zeile hinzu, die mit dem Kontext der Aktivität initialisiert wird. PublishProgress () wird vom Rückruf des Interface ParseListener aufgerufen. Da ich jedoch in doInBackground () nur log.v mache, fand ich dies zunächst unnötig, um es überhaupt aufzurufen.

EDIT 2:

Okay, um ganz klar zu sein, dies ist die andere AsyncTask, die in derselben Aktivität ausgeführt wird und einwandfrei funktioniert.

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

EDIT 3:

Seufz, tut mir leid, dass ich schlecht darin bin, Fragen zu stellen. Aber hier ist die Initialisierung der Aufgaben.

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);
}
SeruK
quelle
Ist es möglich, dass die Aktivität beendet wird, bevor die Aufgabe abgeschlossen ist?
Paul Nikonowicz
Sehr unwahrscheinlich. In diesem Fall würde mein anderer Thread nicht richtig laufen? Und ich habe momentan nur eine Aktivität.
SeruK
2
Meine App hat das gleiche Problem - doInBackground wird entweder nicht oder mit sehr langer Verzögerung aufgerufen. Hier ist meine begrenzte Beobachtung: Genau der gleiche Code funktioniert einwandfrei auf einem Android 2.3.3-Smartphone und einem Android 2.3.3-Emulator, hat jedoch dieses Problem auf einem Android 4.0.3-Tablet und einer Reihe von Android 4.xx-Emulatoren. Es ist sehr verlockend zu schließen, dass dieses Problem in neueren Versionen von Android eingeführt wurde.
Hong
Entschuldigung, aber ich habe vergessen zu erwähnen, dass dieses Problem nur bei der zweiten AsyncTask einer Aktivität auftritt. Die erste AsyncTask funktioniert immer einwandfrei.
Hong
Hong, hast du Matthieus Antwort ausprobiert? Ich bin größtenteils aus dem Geldautomaten des Android-Spiels heraus und habe eine Weile nicht damit gearbeitet, daher kann ich nicht sagen, ob seine Antworten tatsächlich funktionieren. Wenn es nicht für Sie ist, dann war es vielleicht schlecht von mir, seine Antwort zu akzeptieren ...
SeruK

Antworten:

107

Matthieus Lösung wird für die meisten gut funktionieren, aber einige können Probleme haben; es sei denn, Sie graben in viele Links, die hier oder im Internet bereitgestellt werden, wie Anders Göransson Erklärung. Ich versuche hier einige andere Lesevorgänge zusammenzufassen und die Lösung schnell zu erklären, wenn executeOnExecutor immer noch in einem einzelnen Thread arbeitet ...

Das Verhalten von AsyncTask().execute();hat sich durch Android-Versionen geändert. Bevor Donut- Aufgaben (Android: 1.6 API: 4) seriell ausgeführt wurden, wurden Aufgaben von Donut bis Gingerbread (Android: 2.3 API: 9) parallel ausgeführt. da die Ausführung von Honeycomb (Android: 3.0 API: 11) wieder auf sequentiell umgestellt wurde; AsyncTask().executeOnExecutor(Executor)Für die parallele Ausführung wurde jedoch eine neue Methode hinzugefügt.

Bei der sequentiellen Verarbeitung werden alle Async-Aufgaben in einem einzigen Thread ausgeführt und müssen daher warten, bis die vorherige Aufgabe endet. Wenn Sie Code sofort ausführen müssen, müssen Aufgaben in separaten Threads parallel verarbeitet werden.

Mit AsyncTask ist die serielle Ausführung zwischen Donut- und Honeycomb-Versionen nicht verfügbar, während die parallele Ausführung vor Donut nicht verfügbar ist.

Für die parallele Verarbeitung nach Donut: Überprüfen Sie die Build-Version und verwenden Sie basierend darauf die Methoden .execute () oder .executeOnExecutor (). Der folgende Code kann helfen ...

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:Die Funktion .executeOnExecutor()prüft, ob das targetSdkVersionProjekt kleiner oder gleich HONEYCOMB_MR1(Android: 2.1 API: 7) ist, und erzwingt dann, dass der Executor dies ist THREAD_POOL_EXECUTOR(wodurch Aufgaben nacheinander in Honeycomb ausgeführt werden).
Wenn Sie noch keinen definiert haben , targetSdkVersiondann minSdkVersionwird automatisch als das betrachtet targetSdkVersion.
Wenn Sie Ihre AsyncTask parallel auf Post Honeycomb ausführen, können Sie sie daher nicht targetSdkVersionleer lassen.

Nashe
quelle
1
Sehr gute Antwort. Obwohl Matthieus nicht falsch ist, akzeptiere ich dies, da Sie eine Reihe wichtiger Informationen hinzufügen.
SeruK
@ Nashe Vielen Dank. Es ist wirklich sehr hilfreich. Ich habe 3 Tage mit demselben Problem gekämpft.
Rettete meinen Tag! Ich wünschte, diese Antwort wäre leichter zu finden.
zjk
4
Hey @Nashe, mein Problem ist etwas umständlich. Vor heute habe ich die .execute () -Methode für AsyncTask verwendet und der Code hat perfekt funktioniert. Aber heute habe ich das Problem - das Steuerelement geht nicht in die doInBackground () -Methode. Obwohl die von Ihnen bereitgestellte Lösung funktioniert, bin ich verwirrt, wie sie zuvor ohne die Lösung funktioniert hat. Ich verwende vorher und jetzt die gleichen Geräte.
Ravi Sisodia
Warum sollte das so sein? Warum funktioniert es nicht erwartungsgemäß? :(
Nikolay R
160

Sie sollten diese Antwort überprüfen : https://stackoverflow.com/a/10406894/347565 und den darin enthaltenen Link zu Google-Gruppen.

Ich hatte ein ähnliches Problem wie Sie, immer noch unklar, warum es nicht funktioniert, aber ich habe meinen Code so geändert und das Problem ist weg:

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);
Matthieu
quelle
Ich war sehr schlecht darin, auf diesen Beitrag zurückzublicken. Ich bin längst vom Projekt weggezogen. Ich akzeptiere dies einfach als Antwort, da die Leute zu sagen scheinen, dass es funktioniert.
SeruK
Das hat gerade bei mir funktioniert, aber ich wünschte, ich hätte verstanden, warum. Es funktionierte gut mit Execute, bevor es aufhörte. Eine Sache, die ich mache, ist das Starten einer neuen Asynctask in onPostExecute derselben Asynctask (dh ich rufe sie rekursiv auf). Vielleicht hängt das mit dem Problem zusammen?
Steveh
Ich denke, dies sollte Ihnen alle Erklärungen geben, die Sie brauchen: commonsware.com/blog/2012/04/20/…
Matthieu
1
@ Matthieu: Sir, ich kann Ihnen wirklich nicht genug danken !!! Ich hatte mich stundenlang mit diesem Problem beschäftigt und Ihre Lösung ließ alles wie ein Zauber wirken! Vielen Dank für die fantastische Antwort. Ich wünschte, ich könnte mehr als eine Gegenstimme abgeben !!
Swayam
9

Sie können dies auf zwei Arten tun:

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

Falls Weg 1 für Sie nicht funktioniert, versuchen Sie Weg 2 .

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

Hoffe das wird dir helfen.

Hiren Patel
quelle
Super Dude, es funktioniert, aber die nächsten nachfolgenden Async-Aufgaben, die AsyncTask.THREAD_POOL_EXECUTOR verwenden, schlagen fehl, daher muss ich mit CustomExecutor das gesamte Projekt ändern, denke ich :(
Kumar
@vinu, ich empfehle Ihnen, eine allgemeine asynchrone Aufgabe und eine gemeinsame Methode zum Ausführen von AsyncTask zu verwenden. Hoffe das wird dir helfen.
Hiren Patel
2
Hey das hat mir sehr geholfen! Vielen Dank!
Justin Ebby
6

Ich hatte das gleiche Problem: Ich kann keine zweite AsyncTask ausführen, nachdem ich bei einem ersten "execute" aufgerufen habe: doInBackground wird nur für die erste aufgerufen.

Um zu beantworten, warum dies passiert, überprüfen Sie diese Antwort (unterschiedliches Verhalten je nach SDK)

In Ihrem Fall kann dieses Hindernis jedoch mit executeOnExecutor (verfügbar ab 3.0, das mit 4.0.3 für mich funktioniert hat) vermieden werden. Beachten Sie jedoch die Einschränkungen hinsichtlich der Größe des Thread-Pools und der Warteschlange.

Können Sie so etwas versuchen:

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

Für Ihre Update-Frage: Sie wird in den Dokumenten erläutert. Grundsätzlich nur, um alle Probleme zu vermeiden, die durch Multithreading wie Interferenzen entstehen können.

code7amza
quelle
5

Eine Sache, die ich gerne wissen würde und die Ihr Problem möglicherweise tatsächlich beheben könnte, ist, wo Sie die Instanz Ihrer Klasse instanziieren und die Methode execute () aufrufen. Wenn Sie die Dokumentation zu AsyncTask lesen, müssen beide Vorgänge im Haupt-UI-Thread ausgeführt werden. Wenn Sie Ihr Objekt erstellen und execute von einem anderen Thread aus aufrufen, wird möglicherweise onPreExecute ausgelöst. Ich bin mir hier nicht 100% sicher, aber der Hintergrund-Thread wird nicht erstellt und ausgeführt.

Wenn Sie die Instanz Ihrer AsyncTask aus einem Hintergrundthread oder einer anderen Operation erstellen, die nicht im Haupt-UI-Thread ausgeführt wird, können Sie die folgende Methode verwenden: Activity.runOnUiThread (Runnable)

Sie benötigen Zugriff auf eine Instanz Ihrer ausgeführten Aktivität, um diese Methode aufzurufen. Sie können jedoch Code auf dem UI-Thread von einem anderen Code ausführen, der nicht auf dem UI-Thread ausgeführt wird.

Hoffe das macht Sinn. Lassen Sie mich wissen, ob ich mehr helfen kann.

David

David C. Sainte-Claire
quelle
Hinzufügen zu Ihrer Antwort Dieser Thread hat eine interessante Antwort stackoverflow.com/questions/4080808/… .
Manjusg
Danke für die tolle Antwort! Ich habe dies erhöht, weil ich denke, dass dies ein häufiges Problem für AsyncTask-Anfänger sein könnte. Leider ist es nicht ganz die richtige Antwort für dieses Problem. Beide Klassen werden in onCreate () einer Aktivität instanziiert, das auf dem Haupt-UI-Thread ausgeführt wird. Ich habe nur eine Aktivität in diesem Projekt.
SeruK
@manjusg Ich habe die ganze Zeit über nachgedacht, dass es etwas damit zu tun hat, dass AsyncTask instabil ist, vielleicht besonders, wenn mehrere gleichzeitig ausgeführt werden. Wenn ja warum?
SeruK
Ich weiß nicht genau, welche Richtlinien SO hat, um dreimal hintereinander schnell zu posten , aber ich habe dies im anderen Thread gefunden ... foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html "AsyncTask verwendet eine statische interne Arbeitswarteschlange mit einem fest codierten Limit von 10 Elementen. " Dies könnte etwas beweisen, aber ich habe nur zwei Instanzen von AsyncTask-Unterklassen! Ich würde wirklich gerne vermeiden, die normalen Threading-Methoden zu verwenden, da irgendwann eine Menge Parsing in dere durchgeführt wird.
SeruK
2

Android ist brutal! Ich kann das nicht glauben, welche Flakey-Implementierung sich von Tag zu Tag ändert. An einem Tag ist es ein einzelner Thread, am nächsten 5 ist der andere 128.

Wie auch immer, hier ist ein fast geringerer Ersatz für die serienmäßige AsyncTask. Sie können es sogar AsyncTask nennen, wenn Sie möchten, aber um Verwirrung zu vermeiden, heißt es ThreadedAsyncTask. Sie müssen executeStart () anstelle von execute aufrufen, da execute () final ist.

/**
 * @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); 
    }
}
Kevin Parker
quelle
Warten Sie, Sie sagen nicht, dass einige Android-Versionen asynchrone Operationen auf einen Thread beschränken, oder? Das wäre unglaublich dumm. (Ich bin schon eine Weile nicht mehr im Android-Spiel. :))
SeruK
Ja, unter Android 3.0+ erhalten Sie nur einen Thread-Pool von einem, wenn Sie AsyncTask.THREAD_POOL_EXECUTOR nicht verwenden. Probieren Sie es selbst aus, zwei AsyncTask's und schlafen Sie einfach auf dem doInBackground. Aus der Android AsyncTask-Dokumentation: "Ab HONEYCOMB werden Aufgaben in einem einzelnen Thread ausgeführt, um häufige Anwendungsfehler durch parallele Ausführung zu vermeiden."
Kevin Parker
1

Ich weiß, dass dies für den Thread sehr spät sein kann, aber es gibt einen Grund, warum es bei späteren Android-Emulatoren nicht funktioniert. Als asynctask eingeführt wurde, ließ Android Sie immer nur eine nach der anderen ausführen. Einige Zeit später, da ich nicht sicher bin, welche Version es Ihnen ermöglichte, mehrere Asynctasks gleichzeitig auszuführen, verursachte dies Probleme in vielen Apps, und so wurden sie in Honeycomb + nur wieder verwendet Zulassen, dass jeweils eine Asynctask ausgeführt wird. Es sei denn, Sie ändern den Thread-Pool manuell. Hoffe, das klärt ein oder zwei Dinge für die Menschen.

Fre AkyTurtle-Records
quelle
0

Ich denke, es ist der SDK. ich hatte das gleiche problem und nachdem ich target sdk von 15 auf 11 geändert hatte, funktioniert alles perfekt.

Mit sdk15 wird doInBackground niemals aufgerufen, obwohl AsyncTask.Status ausgeführt wird. Ich denke, es hat etwas mit dem UI-Thread zu tun.

Phobus
quelle
Ich kann weder leugnen noch bestätigen, da ich momentan nicht wirklich die Zeit habe, es zu testen. Ich kann nur sagen, dass ich SDK 15 verwendet habe, es ist also sehr wahrscheinlich.
SeruK
0

Basierend auf Matthieus Antwort finden Sie unten eine Hilfsklasse , um Ihre AsyncTaskabhängig von der SDK-Version korrekt auszuführen, um zu vermeiden, dass Code in Ihrer Anwendung dupliziert wird:

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

}

Anwendungsbeispiel:

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

...

final MyTask myTask = new MyTask();
new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);
LG
quelle