從新瞭解Android中Handler

簡介

知識的獲取須要知其一,還要知其二,有機會還要知其三啦。java

在Android中常常會使用到Handler,或者在View的更新中常常能見到Handler的代碼,只有主線程才能操做UI這是咱們的固定思惟。咱們一般在子線程作完耗時操做後,通知主線程去更新UI,通常都是經過Handler發送消息,主線程接收消息後執行對應的邏輯。android

那麼不妨問問幾個問題:markdown

  1. Handler是什麼?
  2. 爲何要用Handler機制去更新UI呢?
  3. 消息的發送與處理經歷了哪些?
  4. 非UI線程真的不能更新UI嗎?(第二篇講)

若是能一下說明白,大佬請慢些走,手動奉杯茶【滑稽】。網絡

Handler是什麼?

handler是android給咱們提供用來更新UI的一套機制,也是一套消息處理機制(這是重點),咱們能夠發送消息,也能夠經過它處理消息。在系統的源碼中,activity也會經過Handler機制發送消息回調生命週期函數。併發

爲何要用Handler機制去更新UI呢

咱們常常會在子線程去處理大量運算、網絡請求、圖片壓縮等操做。當咱們操做完,要通知主線程操做結果,能夠進行下一步行動了。這時候又有一個問題,若是幾個線程同時去通知主線程呢,那麼必然會有併發問題,咱們怎麼避免這個問題又不會影響性能呢?(加同步鎖會影響性能)函數

Google大哥仍是瞭解開發者意願的,搬出來了Handler機制,內部有Looper、MessageQueue兩個重要角色,光看名字咱們就知道這是一個消息隊列,經過隊列方式去對UI進行更新。很好解決了多個子線程的併發問題,還不影響性能。oop

消息的發送與處理經歷了哪些?

第一步確定是從new Handler()開始了。Looper、MessageQueue、CallBack,能夠看到咱們熟悉的3個對象都有了。post

在經過Looper.myLooper得到looper對象時,若是Looper爲空,那麼會拋出異常(提示:不能在未調用Looper.prepare()的線程裏建立Handler)。在Android的主線程中,啓動時便已調用過,能夠直接建立Handler,可是在其它子線程裏,直接建立Handler是會致使應用崩潰的。性能

avatar

接着從Looper中獲取了MessageQueue對象了,存儲消息都是保存在這裏面。ui

Looper對象

進入myLooper方法,能夠看到對象是經過ThreadLocal獲取的,ThreadLocal是一個線程局部變量(map結構),和普通變量的不一樣在於,每一個線程持有這個變量的一個副本,能夠獨立調用set和get方法,而且線程之間不會發生衝突(避免併發)。

/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */
public static @Nullable Looper myLooper() {
  return sThreadLocal.get();
}

複製代碼

既然知道了ThreadLocal是什麼,那麼就去看看在哪調用它的set方法。

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.prepare()建立了一個Looper對象,而後將對象存入到了sThreadLocal線程局部變量中。一樣在一個線程中重複調用了prepare()方法,發現sThreadLocal已經不爲空了,是會拋出異常的,提示「一個線程中只能有一個Looper對象」。

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

這說明建立的Looper都是獨立且惟一的,到這裏Looper的建立過程也就完畢了。

消息的接收與發送

handler.sendEmptyMessage()發送一個空消息,實際上幾個發送消息方法最終調用的都是sendMessageAtTime()方法,而後執行queue.enqueueMessage()將消息存入隊列中(注意看,target默認指向的是Handler本身)。

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;  //target指向的是Handler本身
  if (mAsynchronous) {
    msg.setAsynchronous(true);
  }
  return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼

接下來再看看消息的發送,咱們知道發送是調用的Looper.loop()方法,直接進入源碼。

看到先獲取並判斷Looper對象是否爲空,而後拿到MessageQueue消息隊列。在下面定義了一個死循化,不停的從隊列中取值,若是爲空則return掉,不爲空則執行msg.target.dispatchMessage(msg)。敲黑板了!!!,這裏的target以前說過默認狀況是Handler,將信息又丟給Handler本身處理。

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;

  for (;;) {
    Message msg = queue.next(); // might block
    if (msg == null) {
      // No message indicates that the message queue is quitting.
      return;
    }
		//....省略一大堆代碼
    try {
      msg.target.dispatchMessage(msg);
      dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    } finally {
      if (traceTag != 0) {
        Trace.traceEnd(traceTag);
      }
    }
    //...再省略一大堆代碼

    msg.recycleUnchecked();
  }
}
複製代碼

在這裏咱們就介紹完了Message的存儲與發送,在Handler本身去處理消息時,會判斷callback是否爲空(去看看post(runnable)),爲空則執行handleMessage(msg)方法,讓咱們本身跟本身去完了。

public void dispatchMessage(Message msg) {
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}
複製代碼

總結

彩蛋來了,來一篇圖解Handler吧。

我(Handler)對十元(Looper)說:「我喜歡你,作我女友吧(發送消息給Looper)」,十元接收到後確定不能立馬就答應我啊,女孩子要矜持嘛,要想想:「我要不要接受他,他之後還會不會對我好,我不接受他他會不會很傷心,萬一這帥哥跳河了怎麼辦(Looper.loop)」。而後過了一會想通了,以爲嗯能夠的,回答我:「好吧(Looper分發消息給Handler)」。這回到我了,我接收到消息後確定不是結束啊,還要想着兩人的將來的,如今的年輕人又不是肯定關係就能結婚的,固然是選擇繼續關愛、呵護她呀(消息處理)。 (未完待續第二篇)

相關文章
相關標籤/搜索