Android 必備知識之Handler機(jī)制

Handler是我們常用的一種通信方式,可用于子線程更新UI

對于Handler我們需要知道的有以下四種對象

  • Handler:用于分發(fā)消息
  • MessageQueue:用于存儲Message
  • Message:通信的消息
  • Looper:一個消息循環(huán)機(jī)制

一:Handler的基本使用

我們使用Handler的時候,一般是這么使用的(有多種使用方式,這里簡單的舉例)

// MainActivity.kt
val handler: Handler = Handler(
  Handler.Callback {
    when (it.what) {
      1 -> {
        // doSomething
      }
      else -> {}
    }
    true         
  }
)

// 在其他地方調(diào)用
val obtain = Message.obtain()
obtain.what = 1
handler.sendMessage(obtain)

先新建一個Handler,并聲明回調(diào),在回調(diào)中根據(jù)傳遞的數(shù)據(jù)做不同的動作,之后在任何一個地方都可以使用handler對象的sendMessage()方法,發(fā)送數(shù)據(jù),數(shù)據(jù)會在之前聲明的回調(diào)中接收到

二:源碼分析

為什么我們的Handler可以這么使用呢?

對于這個問題,我覺得我們學(xué)習(xí)Android的有必要知道一下,起碼以后面試可能也有用處。

我們可以一步一步來分析

首先我們使用Handler就必須創(chuàng)建一個Handler,我們查看Handler的構(gòu)造函數(shù)

// Handler.java

// 本例用的即此構(gòu)造函數(shù)
public Handler(Callback callback) {
  this(callback, false);
}

public Handler(Callback callback, boolean async) {
  ...
  mLooper = Looper.myLooper(); // 1
  if (mLooper == null) {
    throw new RuntimeException(
      "Can't create handler inside thread " + Thread.currentThread()
        + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

此時我們可以看到1位置上Looper.myLooper()這是去獲取Looper對象,Looper是一個循環(huán)機(jī)制,可以不斷的從隊列中拿到消息進(jìn)行發(fā)送,我們來看下這個對象是怎么獲取到的

// Looper.java

public static @Nullable Looper myLooper() {
  return sThreadLocal.get();
}

看到這里,可能大家會疑惑這sThreadLocal是什么鬼,其實這是一個ThreadLocal對象,我們可以簡單理解下,就是它可以讓我們每個線程都有一個屬于自己的局部變量,隸屬于線程,在當(dāng)前線程中使用sThreadLocal.get()都可以拿到線程中唯一的局部變量,可能介紹的不是那么容易懂,其實它的源碼也很簡單,有興趣的大家可以去看看,絕對讓你一下子就能理解的。

注意:使用Looper也是有規(guī)定的,需要先調(diào)用Looper.prepare(),然后才能調(diào)用Looper.loop(),為什么?請看下面的解釋

一般我們使用Looper,我們?nèi)绻胍屪泳€程一直運(yùn)行,并隨時在子線程中處理數(shù)據(jù)
則可以這么做

Thread {
  Looper.prepare()
    mThreadHandler = Handler(Looper.myLooper()) {
      // 處理數(shù)據(jù)
    
      true
    }
    Looper.loop()
}.start()

如果我們不使用Looper.prepare(),繼而使用Looper.loop()則程序會報錯

// Looper.java

public static void loop() {
  final Looper me = myLooper();
  if (me == null) {
     throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  }
  ...
}

那么問題來了,我們的Handler是在主線程實現(xiàn)的,既然能一直接收消息,證明主線程是有Looper存在,并且執(zhí)行了Looper.prepare()Looper.loop(),不然程序就會奔潰,那么這玩意到底是怎么在哪里創(chuàng)建出來的呢?

答案是在ActivityThread類的main函數(shù)創(chuàng)建出來的

// ActivityThread.java

public static void main(String[] args) {
  ...     
  Looper.prepareMainLooper(); // 1
  ...
  ActivityThread thread = new ActivityThread();
  thread.attach(false, startSeq);
  if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
  }
  if (false) {
    Looper.myLooper().setMessageLogging(new
    LogPrinter(Log.DEBUG, "ActivityThread"));
  }
  // End of event ActivityThreadMain.
  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  Looper.loop(); // 2
  throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看到這里的12分別調(diào)用了我們想要看到的Looper.prepare()Looper.loop(),至于這個ActivityThread是什么,我們現(xiàn)在只需要知道它就是一個手機(jī)中每個應(yīng)用的入口,正如我們運(yùn)行Java程序時需要調(diào)用main()入口函數(shù)一樣,這個類中也提供了一個main()函數(shù),就是應(yīng)用的入口函數(shù),在這個入口函數(shù)中,就創(chuàng)建了我們需要的主線程Looper對象,所以我們直接在主線程實例化Handler對象就是使用的這個Looper對象,并可以一直接收消息

既然Looper循環(huán)有了,Handler就能一直接收消息了。

接下來我們從發(fā)送數(shù)據(jù)開始看起

val obtain = Message.obtain()
obtain.what = 1
handler.sendMessage(obtain)

以上就是創(chuàng)建一個Message然后發(fā)送出去,我們可能也常用handler.post(Runnable r),其實查看源碼可以得知不管是用哪一種方式去發(fā)送數(shù)據(jù),最后調(diào)用到Handler對象的一個方法

// Handler.java

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; // 1
    if (mAsynchronous) {
      msg.setAsynchronous(true);
     }
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看到1這里需要發(fā)送的msg的target指定了這個Handler(這里要記住),然后再用這個queue發(fā)送一個數(shù)據(jù)出去
那么這里又疑惑了,這個queue又是哪里來的,也就是HandlermQueue對象從哪里來,其實上面的代碼就已經(jīng)提示了,在Handler的初始化構(gòu)造函數(shù)里面就有

// Handler.java

mQueue = mLooper.mQueue;

這時候我們可以理一下就是,Looper一直循環(huán),Looper里面有一個消息隊列MessageQueue,發(fā)送數(shù)據(jù)都是通過這個Looper里面的一個對象mQueue發(fā)送的

接下來我們看這個消息隊列是怎么發(fā)數(shù)據(jù)的,通過queue.enqueueMessage(msg, uptimeMillis);我們?nèi)タ聪?code>MessageQueue類里面的enqueueMessage方法

// MessageQueue.java

boolean enqueueMessage(Message msg, long when) {
  ...
  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;

    // 1,先獲取當(dāng)前準(zhǔn)備要發(fā)送的消息
    Message p = mMessages; 
    boolean needWake;
    // 2,如果當(dāng)前要準(zhǔn)備的消息是空的,或者我們設(shè)置的時間是0
    //   意思就是此時消息隊列里面是空的,或者我們設(shè)置的時間小于這個當(dāng)前準(zhǔn)備發(fā)送的消息
    //   則交換下順序,把發(fā)送的消息和當(dāng)前準(zhǔn)備發(fā)送的消息調(diào)換一下順序,即先發(fā)我們要發(fā)送的消息
    if (p == null || when == 0 || when < p.when) { // 2
      // 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();

      // 3,如果不能插入到當(dāng)前準(zhǔn)備發(fā)送的消息前面,則往后面依次進(jìn)行排序,根據(jù)這個when時間
      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;
}

代碼中標(biāo)明了注釋,仔細(xì)品品就知道這個queue.enqueueMessage(msg, uptimeMillis);,與其說是發(fā)送數(shù)據(jù),更確切的應(yīng)該說是對消息根據(jù)時間進(jìn)行排序

那么我們真正的發(fā)送消息是在哪里呢?

我們知道Looper是一個可以帶有循環(huán)機(jī)制的東西,Looper.loop即開始進(jìn)行循環(huán),我們可以看下這里面的代碼,這個方法里面有很長代碼,大家要挑重點看

// Looper.java
 
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;
        ...
        // 1,此時一直循環(huán)從隊列中獲取消息
        for (;;) {
            
            // 2,獲取消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ...
            try {
                // 3,開始分發(fā)消息,此時的target即Handler
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
        }
    }

我們看代碼注釋2這個地方,調(diào)用queue.next,取出一個消息,然后注釋3這塊就是使用調(diào)用消息里面的target對象,由前面的代碼分析可以知道這個target就是發(fā)送消息的handler,此時就把消息分發(fā)出去了,接下來我們可以查看下這個MessageQueuenext方法,和HandlerdispatchMessage方法

MessageQueuenext方法

// MessageQueue.java

Message next() {
  ...
  for (;;) {
    if (nextPollTimeoutMillis != 0) {
      Binder.flushPendingCommands();
    }
    // 1,nativePollOnce 方法用于“等待”, 直到下一條消息可用為止,當(dāng)沒有消息的時候會阻塞住
    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;

      // 一般的,我們的msg.target是不會為空的,所以可以略過這條判斷
      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());
       }
       // 2,開始取消息
       if (msg != null) {
         // 情況一:還沒到消息需要發(fā)送的時間,則等待這個間隔時間
         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);
         } 
         // 情況二:到了發(fā)送的時間了,則把下一個需要發(fā)送的消息賦值給mMessage
         //   并返回這個時候需要發(fā)送的消息
         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();
           // 返回需要發(fā)送的消息
           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;
       }
       ...
     }
   }
}

這個方法注釋寫了很清楚,總的來說就是沒有消息則阻塞等待,有消息的就取出消息

HandlerdispatchMessage方法

// Handler.java

public void dispatchMessage(Message msg) {
  // 一般我們還可以使用Handler.post(Runnable r)方法發(fā)送消息
  // 這個時候的msg.callback 就是這個 r
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}

這個方法就簡單了,如果我們設(shè)置了回調(diào),則會回調(diào)出去,具體執(zhí)行什么方法要看你怎么發(fā)送這個消息。

上面我們舉的例子是這樣發(fā)的

val obtain = Message.obtain()
obtain.what = 1
handler.sendMessage(obtain)

如果使用上面的這個handler.sendMessage(Message msg)則會傳消息給我們初始化的時候的回調(diào)函數(shù)

val handler: Handler = Handler(
  Handler.Callback {
    when (it.what) {
      1 -> {
        // doSomething
      }
      else -> {}
    }
    true         
  }
)

還有另外一種發(fā)送消息的方法,就是使用post(Runnable r)發(fā)送

handler?.post {
  object : Runnable {
    override fun run() {

    }
  }
}

我們可以看下這個post方法

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

可以看到這里有個賦值,m.callback = r,所以我們post發(fā)送數(shù)據(jù)后對應(yīng)的就是運(yùn)行這個r

public void dispatchMessage(Message msg) {
  // 一般我們還可以使用Handler.post(Runnable r)方法發(fā)送消息
  // 這個時候的msg.callback 就是這個 r
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    ...
  }
}

private static void handleCallback(Message message) {
  message.callback.run();
}

直接運(yùn)行rrun方法

解決了上面兩種數(shù)據(jù)接收方式,還剩下一個handleMessage(msg)

public void dispatchMessage(Message msg) {
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    ...
    handleMessage(msg);
  }
}

這個用于我們創(chuàng)建一個類,繼承自Handler

class Myhandler : Handler() {
  override fun handleMessage(msg: Message?) {
  }
}

這個時候使用如下方式發(fā)送消息,則會在上面重寫的方法handleMessage中接收到消息

val obtain = Message.obtain()
obtain.what = 1
myHandler.sendMessage(obtain)

分析到這,基本算是把Handler機(jī)制給理清了,看下來可能有點吃力,不過如果大家自己跟著源碼一步一步走下去,自己也是可以理解清楚的

總結(jié)

Handler機(jī)制:

1,Looper消息循環(huán)機(jī)制 創(chuàng)建

使用Looper.prepare(),創(chuàng)建一個Looper實例,并保存在Looper的一個靜態(tài)變量sThreadLocal中,然后用Looper.loop()開啟消息循環(huán),此時創(chuàng)建的Handler可以傳指定的Looper實例進(jìn)去,如果是主線程的創(chuàng)建的Handler,則已經(jīng)有Looper實例了,此時就不需要創(chuàng)建Looper實例

2,Handler發(fā)送消息

使用Handler.sendMessage(Message msg),或者Handler.post(Runnable r)發(fā)送消息,此時會調(diào)用HandlerenqueueMessage(),里面調(diào)用了MessageQueueenqueueMessage,此時就把消息根據(jù)發(fā)送時間進(jìn)行了排序

3,Looper.loop() 循環(huán),檢測消息并發(fā)送

真實的發(fā)送放在了Looper.loop()方法里面,此時會調(diào)用Message.next()方法,獲取一個消息,如果沒有消息,next()方法則會阻塞等待,釋放CPU,一旦有消息,則會喚醒,并在Looper.loop()方法里面進(jìn)行消息的發(fā)送,然后消息發(fā)送又調(diào)用了Handler.dispatchMessage()方法,此時我們就可以接收到消息了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容