Frage

Ich habe eine AsyncTask die einige Daten abruft und aktualisiert dann die UI mit diesen neuen Daten. Es ist seit Monaten arbeiten gut, aber ich vor kurzem ein Merkmal, dass zeigt eine Benachrichtigung hinzugefügt, wenn neue Daten vorhanden sind. Nun, wenn meine Anwendung durch die Benachrichtigung gestartet wird, manchmal bekomme ich diese Ausnahme und onPostExecute nicht aufgerufen wird.

Dies ist, was passiert, wenn die App gestartet wird:

1) Erweitern Sie die Benutzeroberfläche und finden Ansichten

2) Abbrechen des Alarms (durch AlarmManager), die prüft, ob neue Daten und den Alarm zurückgesetzt. (Dies ist so, dass, wenn der Benutzer deaktiviert den Alarm es vor der nächsten Zeit abgebrochen wird er / sie neu startet.)

3) Starten Sie den AsyncTask. Wenn die App aus der Meldung ins Leben gerufen wurde, in einem wenig der Daten übergibt und dann die Meldung gelöscht werden.

Ich bin fest auf das, was verursacht diese Ausnahme könnte. Es scheint, dass die Ausnahme von der AsyncTask Code ist, so bin ich nicht sicher, wie ich das Problem beheben kann.

Danke!

Hier ist die Ausnahme:

I/My App(  501): doInBackground exiting
W/MessageQueue(  501): Handler{442ba140} sending message to a Handler on a dead thread
W/MessageQueue(  501): java.lang.RuntimeException: Handler{442ba140} sending message to a Handler on a dead thread
W/MessageQueue(  501):  at android.os.MessageQueue.enqueueMessage(MessageQueue.java:179)
W/MessageQueue(  501):  at android.os.Handler.sendMessageAtTime(Handler.java:457)
W/MessageQueue(  501):  at android.os.Handler.sendMessageDelayed(Handler.java:430)
W/MessageQueue(  501):  at android.os.Handler.sendMessage(Handler.java:367)
W/MessageQueue(  501):  at android.os.Message.sendToTarget(Message.java:348)
W/MessageQueue(  501):  at android.os.AsyncTask$3.done(AsyncTask.java:214)
W/MessageQueue(  501):  at java.util.concurrent.FutureTask$Sync.innerSet(FutureTask.java:252)
W/MessageQueue(  501):  at java.util.concurrent.FutureTask.set(FutureTask.java:112)
W/MessageQueue(  501):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:310)
W/MessageQueue(  501):  at java.util.concurrent.FutureTask.run(FutureTask.java:137)
W/MessageQueue(  501):  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
W/MessageQueue(  501):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
W/MessageQueue(  501):  at java.lang.Thread.run(Thread.java:1096)

EDIT: Hier ist meine onCreate Methode in meiner Haupttätigkeit (die man durch die Benachrichtigung geöffnet). Es gibt einige onClickListeners, dass ich um Platz zu sparen weggelassen. Ich glaube nicht, dass sie keine Auswirkungen haben, da die Tasten sie gebunden sind, um nicht gedrückt wird.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Call the parent

    setContentView(R.layout.main); // Create the UI from the XML file

    // Find the UI elements
    controls = (SlidingDrawer) findViewById(R.id.drawer); // Contains the
    // buttons
    // comic = (ImageView) findViewById(R.id.comic); // Displays the comic
    subtitle = (TextView) findViewById(R.id.subtitleTxt); // Textbox for the
    // subtitle
    prevBtn = (Button) findViewById(R.id.prevBtn); // The previous button
    nextBtn = (Button) findViewById(R.id.nextBtn); // The next button
    randomBtn = (Button) findViewById(R.id.randomBtn); // The random button
    fetchBtn = (Button) findViewById(R.id.comicFetchBtn); // The go to specific id button
    mostRecentBtn = (Button) findViewById(R.id.mostRecentBtn); // The button to go to the most recent comic
    comicNumberEdtTxt = (EditText) findViewById(R.id.comicNumberEdtTxt); // The text box to Zooming image view setup
    zoomControl = new DynamicZoomControl();

    zoomListener = new LongPressZoomListener(this);
    zoomListener.setZoomControl(zoomControl);

    zoomComic = (ImageZoomView) findViewById(R.id.zoomComic);
    zoomComic.setZoomState(zoomControl.getZoomState());
    zoomComic.setImage(BitmapFactory.decodeResource(getResources(), R.drawable.defaultlogo));
    zoomComic.setOnTouchListener(zoomListener);

    zoomControl.setAspectQuotient(zoomComic.getAspectQuotient());

    resetZoomState();

    // enter the new id
    imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // Used to hide the soft keyboard

    Log.i(LOG_TAG, "beginning loading of first comic");
    int notificationComicNumber = getIntent().getIntExtra("comic", -1);
    Log.i(LOG_TAG, "comic number from intent: " + notificationComicNumber);
    if (notificationComicNumber == -1) {
        fetch = new MyFetcher(this, zoomComic, subtitle, controls, comicNumberEdtTxt, imm, zoomControl);
        fetch.execute(MyFetcher.LAST_DISPLAYED_COMIC);
    } else {
        fetch = new MyFetcher(this, zoomComic, subtitle, controls, comicNumberEdtTxt, imm, zoomControl);
        fetch.execute(notificationComicNumber);
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
    }
    Log.i(LOG_TAG, "ending loading of new comic");

    Log.i(LOG_TAG, "first run checks beginning");
    // Get SharedPreferences
    prefs = getSharedPreferences("prefs", Context.MODE_PRIVATE);

    // Check if this is the first run of the app for this version
    if (prefs.getBoolean("firstRun-" + MAJOR_VERSION_NUMBER, true)) {
        prefs.edit().putBoolean("firstRun-" + MAJOR_VERSION_NUMBER, false).commit();
        firstRunVersionDialog();
    }

    // Check if this is the first run of the app
    if (prefs.getBoolean("firstRun", true)) {
        prefs.edit().putBoolean("firstRun", false).commit();
        firstRunDialog();
    }
    Log.i(LOG_TAG, "First run checks done");

            // OnClickListener s for the buttons omitted to save space

EDIT 2: Ich habe durch Android-Quellcode Graben aufzuspüren, wo die Ausnahme herkommt. Dies ist Linien 456 und 457 von sendMessageAtTime in Handler:

msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);

Und das ist enqueueMessage von MessageQueue:

    final boolean enqueueMessage(Message msg, long when) {
        if (msg.when != 0) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                this.notify();
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                this.notify();
            }
        }
        return true;
    }

Ich bin ein wenig verwirrt darüber, was mQuiting ist, aber es sieht aus wie die vorherige Zeit enqueueMessage genannt wurde msg.target war null.

War es hilfreich?

Lösung

Um Jonathan Perlow-Lösung auf den Bug verallgemeinert er speziell identifiziert, verwende ich die folgende in jeder Klasse, dass Verwendungen AsyncTask. Der Looper / Handler / Post ist, wie Sie etwas auf dem UI-Thread irgendwo in einer Android-App ohne den Weg nach unten eine Verbindung zu einer Aktivität oder anderen Kontext ausgeführt werden können. Fügen Sie diese statische Initialisierung Block innerhalb der Klasse:

{ // https://stackoverflow.com/questions/4280330/onpostexecute-not-being-called-in-asynctask-handler-runtime-exception
    Looper looper = Looper.getMainLooper();
    Handler handler = new Handler(looper);
    handler.post(new Runnable() {
      public void run() {
        try {
          Class.forName("android.os.AsyncTask");
        } catch (ClassNotFoundException e) {
          e.printStackTrace();
        }
      }
    });
}

Wir hatten in das Problem führen, wenn sie versuchen Unit-Tests zu erhalten auszuführen. Ich fand eine Abhilfe für das, aber hatte nicht speziell das Problem erkannt. Wir wussten nur, dass AsyncTask <> in Android JUnit-Test zu verwenden versuchen, verursacht OnPostExecute () nicht aufgerufen werden. Jetzt wissen wir, warum.

Dieser Beitrag zeigt, wie Multi-Thread-Asynchron-Code in einem Android-JUnit-Test auszuführen:

Mit CountDownLatch in Android AsyncTask-basierten JUnit-Tests

Für die Verwendung mit nicht-UI-Unit-Tests, habe ich eine einfache Unterklasse von android.test.InstrumentationTestCase. Es hat eine „ok“ Flagge und einen CountDownLatch. reset () oder zurückgesetzt (COUNT) erzeugt einen neuen CountDownLatch ({1, count}). gut () setzt ok = true, count-- und calls.countDown () auf der Klinke. schlecht () setzt ok = false, und zählt den ganzen Weg hinunter. waitForIt (Sekunden) wartet auf Timeout oder der Coundown Latch auf Null. Dann ruft sie assertTrue (ok).

Dann Tests sind wie:

someTest() {
  reset();
  asyncCall(args, new someListener() {
    public void success(args) { good(); }
    public void fail(args) { bad(); }
  });
  waitForIt();
}

Aufgrund der AsyncTask statische Initialisierung Fehler mussten wir unsere eigentlichen Tests laufen im Inneren ein Runnable zu runTestOnUiThread () übergeben. Mit dem richtigen statischer Initialisierung, wie oben, ist dies nicht erforderlich sein sollte, es sei denn, der Anruf Bedürfnisse getestet wird auf dem UI-Thread ausgeführt werden.

Die anderen Idiom ich jetzt Gebrauch zu testen, ob der aktuelle Thread der UI-Thread und dann unabhängig von der angeforderten Aktion auf dem entsprechenden Thread ausgeführt. Manchmal macht es Sinn, den Anrufer zu ermöglichen sync vs. async zu verlangen, überschrieben, wenn notwendig. Zum Beispiel sollen Netzwerkanforderungen immer auf einem Hintergrund-Thread ausgeführt werden. In den meisten Fällen ist AsyncTask Threadpooling für diese perfekt. nur erkennen, dass nur eine bestimmte Anzahl auf einmal ausgeführt wird, blockiert zusätzliche Anforderungen. Zur Prüfung, ob der aktuelle Thread ist der UI-Thread:

boolean onUiThread = Looper.getMainLooper().getThread() == Thread.currentThread();

Verwenden Sie dann eine einfache Unterklasse (nur doInBackground () und OnPostExecute () sind erforderlich) von AsyncTask <> auf einem Nicht-UI-Thread oder handler.post () oder postDelayed () läuft auf dem UI-Thread ausgeführt werden.

Geben dem Anrufer die Option sync oder async Blicke laufen wie (immer einen lokal gültigen onUiThread Wert hier nicht gezeigt, fügen Sie lokale booleans wie oben):

void method(final args, sync, listener, callbakOnUi) {
  Runnable run = new Runnable() { public void run() {
    // method's code... using args or class members.
    if (listener != null) listener(results);
    // Or, if the calling code expects listener to run on the UI thread:
    if (callbackOnUi && !onUiThread)
      handler.post(new Runnable() { public void run() {listener()}});
    else listener();
  };
  if (sync) run.run(); else new MyAsync().execute(run);
  // Or for networking code:
  if (sync && !onUiThread) run.run(); else new MyAsync().execute(run);
  // Or, for something that has to be run on the UI thread:
  if (sync && onUiThread) run.run() else handler.post(run);
}

Auch kann mit AsyncTask sehr einfach und präzise hergestellt werden. Verwenden Sie die Definition von RunAsyncTask.java unten, dann schreiben Code wie folgt:

    RunAsyncTask rat = new RunAsyncTask("");
    rat.execute(new Runnable() { public void run() {
        doSomethingInBackground();
        post(new Runnable() { public void run() { somethingOnUIThread(); }});
        postDelayed(new Runnable() { public void run() { somethingOnUIThreadInABit(); }}, 100);
    }});
.

Oder einfach: neuer RunAsyncTask ( "") ausführen (neu Runnable () {public void run () {doSomethingInBackground ();}});

RunAsyncTask.java:

package st.sdw;
import android.os.AsyncTask;
import android.util.Log;
import android.os.Debug;

public class RunAsyncTask extends AsyncTask<Runnable, String, Long> {
    String TAG = "RunAsyncTask";
    Object context = null;
    boolean isDebug = false;
    public RunAsyncTask(Object context, String tag, boolean debug) {
      this.context = context;
      TAG = tag;
      isDebug = debug;
    }
    protected Long doInBackground(Runnable... runs) {
      Long result = 0L;
      long start = System.currentTimeMillis();
      for (Runnable run : runs) {
        run.run();
      }
      return System.currentTimeMillis() - start;
    }
    protected void onProgressUpdate(String... values) {        }
    protected void onPostExecute(Long time) {
      if (isDebug && time > 1) Log.d(TAG, "RunAsyncTask ran in:" + time + " ms");
      v = null;
    }
    protected void onPreExecute() {        }
    /** Walk heap, reliably triggering crash on native heap corruption.  Call as needed. */  
    public static void memoryProbe() {
      System.gc();
      Runtime runtime = Runtime.getRuntime();
      Double allocated = new Double(Debug.getNativeHeapAllocatedSize()) / 1048576.0;
      Double available = new Double(Debug.getNativeHeapSize()) / 1048576.0;
      Double free = new Double(Debug.getNativeHeapFreeSize()) / 1048576.0;
      long maxMemory = runtime.maxMemory();
      long totalMemory = runtime.totalMemory();
      long freeMemory = runtime.freeMemory();
     }
 }

Andere Tipps

Dies ist aufgrund eines Fehlers in AsyncTask im Android-Framework. AsyncTask.java hat den folgenden Code:

private static final InternalHandler sHandler = new InternalHandler();

Es erwartet, dass diese auf dem Hauptthread initialisiert werden, aber das ist nicht gewährleistet, da es auf initialisiert wird je nachdem, welchem ??Thread ist nun mal die Klasse zu veranlassen, seine statischen Initialisierungen auszuführen. Ich reproduziert dieses Problem, bei dem der Handler einen Worker-Thread verweist.

Ein gemeinsames Muster, das das verursacht passieren wird, um die Klasse IntentService verwenden. Der C2DM Beispielcode tut dies.

Eine einfache Abhilfe ist, den folgenden Code an die Anwendung der onCreate Methode hinzufügen:

Class.forName("android.os.AsyncTask");

Dies zwingt AsyncTask im Hauptthread initialisiert werden. Ich legte einen Fehler auf diesem in der Android-Bug-Datenbank. Siehe http://code.google.com/p/android/issues/ Detail? id = 20915 .

Ich hatte das gleiche Problem auf einem Gerät mit Android 4.0.4 mit dem IntentService und löste es als SDW sagte mit dem Class.forName ( „android.os.AsyncTask“). Das gleiche geschah nicht auf Android 4.1.2, 4.4.4 oder 5.0. Ich frage mich, ob die Google Martin West-Ausgabe aus dem Jahr 2011 aufgelöst.

Ich habe diesen Code in meiner Anwendung onCreate und es hat funktioniert:

    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
        try {
            Class.forName("android.os.AsyncTask");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

Es wäre schön, zu wissen, ob die Version von Android Notwendigkeit zur noch etwas geändert werden.

AsyncTask.execute() müssen ausgeführt auf UI-Thread, dh innerhalb Aktivität.

ich das gleiche Problem haben, so scheint es zu geschehen, wenn die AsyncTask während eines Suspend / Resume läuft.

EDIT: Ja, dachte nicht, daß ich hatte, aber ich verwendet, um dieses http://developer.android .com / guide / Anhang / faq / commontasks.html # Threading immer die AsyncTask auf dem UI-Thread zu starten und das Problem gegangen ist. Das Problem erschien, nachdem ich die Lizenzfunktion hinzugefügt, siggghhhhh

Danke

Auch wenn dies nicht direkt die OPs Frage nicht beantworten, ich glaube, es für die Menschen nützlich sein wird für die Lösung des gleichen Problems suchen, wenn Tests ausgeführt werden.

Insgesamt Peter Knego Antwort Summen es sich gut.

Mein Problem war, und zwar mit einem Test auf einer Klasse außerhalb einer Aktivität ausgeführt wird, die Verwendung von Android AsyncTask für einen API-Aufruf gemacht. Die Klasse arbeitet in der Anwendung, da sie von einer Aktivität verwendet wird, aber ich wollte einen Test machte einen tatsächlichen API-Aufruf vom Test auszuführen.

Während Jonathan Perlow Antwort arbeitete, habe ich nicht wie allein aufgrund Änderungen an meiner Anwendung Einführung zu einem Test.

Also, im Fall eines Test runTestOnUiThread verwendet werden kann (@UiThreadTest nicht verwendet werden, da Sie nicht für ein Ergebnis in einem Test warten können, dass Anwendungen die Anmerkung).

public void testAPICall() throws Throwable {
    this.runTestOnUiThread(new Runnable() {
        public void run() {
            underTest.thisMethodWillMakeUseOfAnAsyncTaskSomehow();
        }           
    }); 

    // Wait for result here *
    // Asserts here
}

Manchmal aber, vor allem in Funktionstests Antwort Jonathan Perlow das scheint die einzige, die Werke zu sein.


* Werfen Sie einen Blick hier zu sehen, wie ein Test für ein Ergebnis warten, um zu pausieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top