title: onPostExecute not being called in AsyncTask (Handler runtime exception)
body: <p>I have an <code>AsyncTask</code> that fetches some data and then updates the UI with this new data. It has been working fine for months, but I recently added a feature that displays a notification when there is new data. Now when my app is launched through the notification, sometimes I get this exception and <code>onPostExecute</code> is not called.</p>
  
  <p>This is what happens when the app is launched:</p>
  
  <p>1) Expand the UI and find views</p>
  
  <p>2) Cancel the alarm (through <code>AlarmManager</code>) that checks for new data and reset the alarm. (This is so that if the user disables the alarm it is cancelled before the next time he/she reboots.)</p>
  
  <p>3) Start the <code>AsyncTask</code>. If the app was launched from the notification, pass in a little bit of the data and then cancel the notification.</p>
  
  <p>I'm stuck on what could be causing this exception. It seems that the exception is from the <code>AsyncTask</code> code, so I'm not sure how I can fix it.</p>
  
  <p>Thanks!</p>
  
  <p>Here is the exception:</p>
  
  <pre><code>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)
  </code></pre>
  
  <p>EDIT: Here is my <code>onCreate</code> method in my main activity (the one opened by the notification). There are some <code>onClickListeners</code> that I omitted to save space. I don't think they should have any effect, since the buttons they are attached to are not being pressed.</p>
  
  <pre><code>@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
  </code></pre>
  
  <p>EDIT 2: I've been digging through Android source code tracking down where the exception is coming from. This is lines 456 and 457 of <code>sendMessageAtTime</code> in <code>Handler</code>:</p>
  
  <pre><code>msg.target = this;
  sent = queue.enqueueMessage(msg, uptimeMillis);
  </code></pre>
  
  <p>And this is <code>enqueueMessage</code> from <code>MessageQueue</code>:</p>
  
  <pre><code>    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 &amp;&amp; !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 &lt; p.when) {
                  msg.next = p;
                  mMessages = msg;
                  this.notify();
              } else {
                  Message prev = null;
                  while (p != null &amp;&amp; p.when &lt;= when) {
                      prev = p;
                      p = p.next;
                  }
                  msg.next = prev.next;
                  prev.next = msg;
                  this.notify();
              }
          }
          return true;
      }
  </code></pre>
  
  <p>I'm a little confused about what <code>mQuiting</code> is, but it looks like the previous time <code>enqueueMessage</code> was called <code>msg.target</code> was null.</p>