1. 程式人生 > >Android Handler類

Android Handler類

相關文章
Android AsyncTask類
Android Handler類
Android IntentService應用

1. Handler類

Handler類被用於非同步訊息處理執行緒,常被用來更新UI執行緒。

  • 一般來說我們會在主執行緒中建立一個Handler的匿名內部類,然後重寫它的handleMessage方法來處理我們的UI操作。

      private final static int UPDATE_TIME = 1;
    
      private TextView mTv;
      private Handler mHandler = new Handler() {
          @Override
          public void handleMessage(Message msg) {
              switch (msg.what) {
                  // 根據what的值處理不同操作
                  case UPDATE_TIME:
                      mTv.setText(getCurrentTime());
                      break;
              }
          }
      };
    

    呼叫Handler的sendMessage方法來發送訊息。

      mHandler.sendEmptyMessage(UPDATE_TIME);
    

    其他類似方法

      sendEmptyMessage(int what)
      sendEmptyMessageAtTime(int what, long uptimeMillis)
      sendEmptyMessageDelayed(int what, long delayMillis)
      sendMessage(Message msg)
      sendMessageAtFrontOfQueue(Message msg)
      sendMessageAtTime(Message msg, long uptimeMillis)
      sendMessageDelayed(Message msg, long delayMillis)
    
  • 線上程中呼叫post方法

      mHandler.post(new Runnable() {
          @Override
          public void run() {
              mTv.setText(getCurrentTime());
          }
      });
    

    其他類似方法

      post(Runnable r)
      postAtFrontOfQueue(Runnable r)
      postAtTime(Runnable r, long uptimeMillis)
      postAtTime(Runnable r, Object token, long uptimeMillis)
      postDelayed(Runnable r, long delayMillis)
    

2. Handler類解析

Android的訊息機制中主要是由Handler,Looper,MessageQueue,Message等組成。

  • Looper主要是prepare()和loop()兩個方法。

    首先是prepare方法,sThreadLocal是一個ThreadLocal物件,可以在一個執行緒中儲存變數。prepare方法不能被呼叫兩次,這保證了一個執行緒中只有一個Looper例項。

      public static void prepare() {
          prepare(true);
      }
    
      private static void prepare(boolean quitAllowed) {
          if (sThreadLocal.get() != null) {
              throw new RuntimeException("Only one Looper may be created per thread");
          }
          sThreadLocal.set(new Looper(quitAllowed));
      }
    

    Looper的建構函式,建立一個訊息佇列。

      private Looper(boolean quitAllowed) {
          mQueue = new MessageQueue(quitAllowed);
          mThread = Thread.currentThread();
      }
    

    loop方法,先獲得當前執行緒的Looper,呼叫訊息佇列的next方法獲取訊息,隨後回撥Handler的dispatchMessage來處理訊息。

      public static void loop() {
          final Looper me = myLooper();
          if (me == null) {
              throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
          }
          final MessageQueue queue = me.mQueue;
    
          // Make sure the identity of this thread is that of the local process,
          // and keep track of what that identity token actually is.
          Binder.clearCallingIdentity();
          final long ident = Binder.clearCallingIdentity();
    
          for (;;) {
              Message msg = queue.next(); // might block
              if (msg == null) {
                  // No message indicates that the message queue is quitting.
                  return;
              }
    
              // This must be in a local variable, in case a UI event sets the logger
              final Printer logging = me.mLogging;
              if (logging != null) {
                  logging.println(">>>>> Dispatching to " + msg.target + " " +
                          msg.callback + ": " + msg.what);
              }
    
              final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
    
              final long traceTag = me.mTraceTag;
              if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                  Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
              }
              final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
              final long end;
              try {
                  msg.target.dispatchMessage(msg);
                  end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
              } finally {
                  if (traceTag != 0) {
                      Trace.traceEnd(traceTag);
                  }
              }
              if (slowDispatchThresholdMs > 0) {
                  final long time = end - start;
                  if (time > slowDispatchThresholdMs) {
                      Slog.w(TAG, "Dispatch took " + time + "ms on "
                              + Thread.currentThread().getName() + ", h=" +
                              msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                  }
              }
    
              if (logging != null) {
                  logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
              }
    
              // Make sure that during the course of dispatching the
              // identity of the thread wasn't corrupted.
              final long newIdent = Binder.clearCallingIdentity();
              if (ident != newIdent) {
                  Log.wtf(TAG, "Thread identity changed from 0x"
                          + Long.toHexString(ident) + " to 0x"
                          + Long.toHexString(newIdent) + " while dispatching to "
                          + msg.target.getClass().getName() + " "
                          + msg.callback + " what=" + msg.what);
              }
    
              msg.recycleUnchecked();
          }
      }
    
      public static @Nullable Looper myLooper() {
          return sThreadLocal.get();
      }
    
  • MessageQueue是訊息佇列

    enqueueMessage中加入訊息。如果當前沒有訊息,或者when小於佇列頭的話,作為新的佇列頭。否則插入到隊伍中。

      boolean enqueueMessage(Message msg, long when) {
          if (msg.target == null) {
              throw new IllegalArgumentException("Message must have a target.");
          }
          if (msg.isInUse()) {
              throw new IllegalStateException(msg + " This message is already in use.");
          }
    
          synchronized (this) {
              if (mQuitting) {
                  IllegalStateException e = new IllegalStateException(
                          msg.target + " sending message to a Handler on a dead thread");
                  Log.w(TAG, e.getMessage(), e);
                  msg.recycle();
                  return false;
              }
    
              msg.markInUse();
              msg.when = when;
              Message p = mMessages;
              boolean needWake;
              if (p == null || when == 0 || when < p.when) {
                  // New head, wake up the event queue if blocked.
                  msg.next = p;
                  mMessages = msg;
                  needWake = mBlocked;
              } else {
                  // Inserted within the middle of the queue.  Usually we don't have to wake
                  // up the event queue unless there is a barrier at the head of the queue
                  // and the message is the earliest asynchronous message in the queue.
                  needWake = mBlocked && p.target == null && msg.isAsynchronous();
                  Message prev;
                  for (;;) {
                      prev = p;
                      p = p.next;
                      if (p == null || when < p.when) {
                          break;
                      }
                      if (needWake && p.isAsynchronous()) {
                          needWake = false;
                      }
                  }
                  msg.next = p; // invariant: p == prev.next
                  prev.next = msg;
              }
    
              // We can assume mPtr != 0 because mQuitting is false.
              if (needWake) {
                  nativeWake(mPtr);
              }
          }
          return true;
      }
    

    next方法中獲取訊息。

      Message next() {
          // Return here if the message loop has already quit and been disposed.
          // This can happen if the application tries to restart a looper after quit
          // which is not supported.
          final long ptr = mPtr;
          if (ptr == 0) {
              return null;
          }
    
          int pendingIdleHandlerCount = -1; // -1 only during first iteration
          int nextPollTimeoutMillis = 0;
          for (;;) {
              if (nextPollTimeoutMillis != 0) {
                  Binder.flushPendingCommands();
              }
    
              nativePollOnce(ptr, nextPollTimeoutMillis);
    
              synchronized (this) {
                  // Try to retrieve the next message.  Return if found.
                  final long now = SystemClock.uptimeMillis();
                  Message prevMsg = null;
                  Message msg = mMessages;
                  if (msg != null && msg.target == null) {
                      // Stalled by a barrier.  Find the next asynchronous message in the queue.
                      do {
                          prevMsg = msg;
                          msg = msg.next;
                      } while (msg != null && !msg.isAsynchronous());
                  }
                  if (msg != null) {
                      if (now < msg.when) {
                          // Next message is not ready.  Set a timeout to wake up when it is ready.
                          nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                      } else {
                          // Got a message.
                          mBlocked = false;
                          if (prevMsg != null) {
                              prevMsg.next = msg.next;
                          } else {
                              mMessages = msg.next;
                          }
                          msg.next = null;
                          if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                          msg.markInUse();
                          return msg;
                      }
                  } else {
                      // No more messages.
                      nextPollTimeoutMillis = -1;
                  }
    
                  // Process the quit message now that all pending messages have been handled.
                  if (mQuitting) {
                      dispose();
                      return null;
                  }
    
                  // If first time idle, then get the number of idlers to run.
                  // Idle handles only run if the queue is empty or if the first message
                  // in the queue (possibly a barrier) is due to be handled in the future.
                  if (pendingIdleHandlerCount < 0
                          && (mMessages == null || now < mMessages.when)) {
                      pendingIdleHandlerCount = mIdleHandlers.size();
                  }
                  if (pendingIdleHandlerCount <= 0) {
                      // No idle handlers to run.  Loop and wait some more.
                      mBlocked = true;
                      continue;
                  }
    
                  if (mPendingIdleHandlers == null) {
                      mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                  }
                  mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
              }
    
              // Run the idle handlers.
              // We only ever reach this code block during the first iteration.
              for (int i = 0; i < pendingIdleHandlerCount; i++) {
                  final IdleHandler idler = mPendingIdleHandlers[i];
                  mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                  boolean keep = false;
                  try {
                      keep = idler.queueIdle();
                  } catch (Throwable t) {
                      Log.wtf(TAG, "IdleHandler threw exception", t);
                  }
    
                  if (!keep) {
                      synchronized (this) {
                          mIdleHandlers.remove(idler);
                      }
                  }
              }
    
              // Reset the idle handler count to 0 so we do not run them again.
              pendingIdleHandlerCount = 0;
    
              // While calling an idle handler, a new message could have been delivered
              // so go back and look again for a pending message without waiting.
              nextPollTimeoutMillis = 0;
          }
      }
    
  • 最後是Handler類

    sendEmptyMessage方法會呼叫sendMessageDelayed方法,指定target是Handler,最終呼叫MessageQueue.enqueueMessage放入訊息列表中。

      public final boolean sendEmptyMessage(int what)
      {
          return sendEmptyMessageDelayed(what, 0);
      }
    
      public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
          Message msg = Message.obtain();
          msg.what = what;
          return sendMessageDelayed(msg, delayMillis);
      }
    
      public final boolean sendMessageDelayed(Message msg, long delayMillis)
      {
          if (delayMillis < 0) {
              delayMillis = 0;
          }
          return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
      }
    
      public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
          MessageQueue queue = mQueue;
          if (queue == null) {
              RuntimeException e = new RuntimeException(
                      this + " sendMessageAtTime() called with no mQueue");
              Log.w("Looper", e.getMessage(), e);
              return false;
          }
          return enqueueMessage(queue, msg, uptimeMillis);
      }
    
      private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
          msg.target = this;
          if (mAsynchronous) {
              msg.setAsynchronous(true);
          }
          return queue.enqueueMessage(msg, uptimeMillis);
      }
    

    post方法,同樣會呼叫sendMessageDelayed方法,只是會指定Message.callback屬性。

      public final boolean post(Runnable r)
      {
         return  sendMessageDelayed(getPostMessage(r), 0);
      }
    
      private static Message getPostMessage(Runnable r) {
          Message m = Message.obtain();
          m.callback = r;
          return m;
      }
    

    dispatchMessage方法處理訊息回撥。

      public void dispatchMessage(Message msg) {
          if (msg.callback != null) {
              handleCallback(msg);
          } else {
              if (mCallback != null) {
                  if (mCallback.handleMessage(msg)) {
                      return;
                  }
              }
              handleMessage(msg);
          }
      }
    
      private static void handleCallback(Message message) {
          message.callback.run();
      }
    
      public void handleMessage(Message msg) {
      }