webentwicklung-frage-antwort-db.com.de

Android SDK AsyncTask doInBackground wird nicht ausgeführt (Unterklasse)

Stand 15.2.2012 Ich habe noch keine gute Erklärung dafür gefunden, noch einen Grund, warum dies nicht funktioniert. Die Lösung, die einer Lösung am nächsten kommt, ist die Verwendung des traditionellen Thread - Ansatzes, aber warum sollte dann eine Klasse verwendet werden, die im Android SDK nicht (scheinbar) funktioniert?

Abends 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");

Nun ist diese Unterklasse in einen kleinen Fehler geraten. Früher hat es ein bisschen Xml-Parsing gemacht, aber als ich bemerkte, dass es doInBackground () nicht heißt, habe ich es Zeile für Zeile abgebaut und letztendlich nur noch folgendes:

@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 irgendwie wird onPreExecute () aufgerufen, aber nicht doInBackground (). Ich habe eine andere AsyncTask, die gleichzeitig im Hintergrund ausgeführt wird.

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

EDIT:

@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 Callback des Interface ParseListener aufgerufen. Da ich jedoch in doInBackground () nichts außer log.v mache, war dies zunächst nicht erforderlich.

EDIT 2:

In Ordnung, nur um ganz klar zu sein, dies ist die andere AsyncTask, die in derselben Aktivität ausgeführt wird und perfekt 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, es tut mir leid. 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);
}
88
SeruK

Die Lösung von Matthieu wird für die meisten gut funktionieren, aber manche können Probleme haben. Wenn nicht in vielen Links oder Links aus dem Web gegraben wird, wie Anders Göransson s Erklärung ..__ Ich versuche, einige andere Reads hier zusammenzufassen und die Lösung schnell zu erläutern, wenn ExecuteOnExecutor immer noch in einem Thread arbeitet ...

Das Verhalten von AsyncTask().execute(); hat sich durch Android-Versionen geändert. Vor Donut _ ​​(Android: 1.6 API: 4)wurden _ Aufgaben seriell ausgeführt, von Donut bis Gingerbread _ ​​(Android: 2.3 API: 9) parallel ausgeführte Aufgaben; seit Honeycomb _ ​​(Android: 3.0 API: 11) wurde die Ausführung auf sequenziell umgestellt; Eine neue Methode AsyncTask().executeOnExecutor(Executor) wurde jedoch für die parallele Ausführung hinzugefügt.

Bei der sequentiellen Verarbeitung laufen alle Async-Tasks in einem einzigen Thread und müssen daher warten, bis die vorherige Task 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 den Versionen Donut und Honeycomb 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 die .execute () - oder .executeOnExecutor () -Methode. Folgender 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() überprüft, ob targetSdkVersion des Projekts kleiner oder gleich HONEYCOMB_MR1 ist (Android: 2.1 API: 7), und zwingt den Executor dazu, THREAD_POOL_EXECUTOR zu sein (wodurch die Aufgaben nacheinander in Honeycomb ausgeführt werden).
Wenn Sie keine targetSdkVersion definiert haben, wird minSdkVersion automatisch als targetSdkVersion betrachtet.
Damit Sie Ihre AsyncTask parallel auf Post Honeycomb ausführen können, können Sie targetSdkVersion nicht leer lassen.

105
Nashe

Sie sollten diese Antwort überprüfen: https://stackoverflow.com/a/10406894/347565 und den Link zu den darin enthaltenen 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);
156
Matthieu

Ich hatte das gleiche Problem: Ich kann keine zweite AsyncTask ausführen, nachdem ich bei einer ersten "Ausführen" 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 mithilfe von executeOnExecutor (verfügbar ab 3.0 für 4.0.3) vermieden werden. Beachten Sie jedoch die Einschränkungen der Thread-Pool-Größe und der Warteschlange.

Kannst du 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 docs .__ erklärt. Im Grunde nur, um alle Probleme zu vermeiden, die durch Multithreading wie Interferenz entstehen können.

6
code7amza

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

Im Falle von Weg 1 funktioniert das nicht für Sie, 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.

5
Hiren Patel

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

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

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

Hoffe das macht Sinn. Lass es mich wissen, wenn ich mehr helfen kann.

David

Android ist brutal! Ich kann das nicht glauben, was für eine flockige Implementierung sich von heute auf heute ändert. Eines Tages ist es ein einzelner Thread, am nächsten sind es 5 und der andere ist 128.

Auf jeden Fall ist der Ersatz für die Standard-AsyncTask fast rückläufig. Sie können es sogar als AsyncTask bezeichnen, wenn Sie wollten, 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); 
    }
}
2
Kevin Parker

Ich weiß, dass dies für den Thread wirklich spät sein kann, aber es gibt einen Grund, warum es auf späteren Android-Emulatoren nicht funktionieren wird. Als Asynctask eingeführt wurde, ließ Android Sie jeweils nur einzeln ausführen. Irgendwann nicht sicher, welche Version Sie verwenden, wurde die gleichzeitige Ausführung mehrerer Asynctasks ermöglicht. Dies führte zu Problemen in einer Vielzahl von Apps. In Honeycomb + wurde nur noch darauf zugegriffen Erlauben, dass jeweils eine Asynktask ausgeführt wird. Es sei denn, Sie ändern den Thread-Pool manuell .. __ Hoffe, das löscht ein oder zwei Dinge für die Menschen.

Basierend auf der Antwort von Matthieu, unterhalb einer helper Klasse, um Ihre AsyncTask abhä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);
0
L. G.

ich denke es ist das sdk. Ich hatte das gleiche Problem und nachdem ich die Ziel-SDK von 15 auf 11 geändert hatte, funktioniert alles perfekt.

mit sdk15 wird das doInBackground nie aufgerufen, obwohl AsyncTask.Status RUNNING ist. Ich denke, es hat etwas mit dem UI-Thread zu tun.

0
phobus