android 子線程和UI線程的交互主要使用Handler的方法進行通信。本文分析Handler機制
Handler 如何使用?
Handler的使用比較簡單
public class MainActivity extends Activity{private Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case 0x01://do somethings}}};@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); handler = new Handler();new Thread(new Runnable(){@Overridepublic void run(){Message message = new Message(); message.what = 0x01; handler.sendMessage(message);}}).start();}}復制代碼
如代碼就是一個簡單的Handler的使用Demo,有如下幾個問題
- Handler 是否可以在子線程中初始化。可以,但是如下代碼執行的話會拋出該錯誤"Can't create handler inside thread that has not called Looper.prepare() ".說
是不能再沒有調用Looper.prepare()的線程中創建Handler。因此如果需要在線程中創建Handler首先調用一下Looper.prepare
new Thread(new Runnable() { @Override public void run() { Handler handler = new Handler(); } }).start();復制代碼
這樣調用將不會拋出異常。
new Thread(new Runnable() { @Override public void run() {Looper.prepare();Handler handler = new Handler(); Looper.loop();} }).start();復制代碼
Looper和Handler的聯系是什么樣的呢?
我們看一下Handler初始化的代碼
public Handler(Callback callback, boolean async) {if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}復制代碼
可以看到如果mLooper是通過Looper.myLooper獲得一個Looper對象,如果Looper對象為空,則拋出上述異常。那Looper.myLooper是如何定義的呢?
public static @Nullable Looper myLooper() {return sThreadLocal.get();}復制代碼
該方法很簡單就是從sThreadLocal對象中獲得Looper對象。如果sThreadLocal中存在就返回Looper,如果沒有就返回null。那Looper是如何存放在sThreadLocal中,
不錯就是Looper.prepare。
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));}復制代碼
可以看到,首先判斷sThreadLocal中是否存在Looper對象,如果已經存在,那么如果還有prepare Looper則拋出異常;否則就新建一個Looper存放到sThreadLocal中。
該代碼同時說明每個線程最多一個Looper對象。
Looper采用ThreadLocal來維護各個線程的Looper對象。ThreadLocal是什么呢?官方定義是:ThreadLocal實現了線程本地存儲。所有線程共享同一個ThreadLocal對象,但不同線程僅能訪問與其線程相關聯的值,一個線程修改ThreadLocal對象對其他線程沒有影響。
我們可以將ThreadLocal理解為一塊存儲區,將這一大塊存儲區分割為多塊小的存儲區,每一個線程擁有一塊屬于自己的存儲區,那么對自己的存儲區操作就不會影響其他線程。對于ThreadLocal,則每一小塊存儲區中就保存了與特定線程關聯的Looper。
主線程中使用Handler時為什么沒有執行Looper.prepare()也可以使用Handler呢?其實在進程啟動的時候我們已經創建了主線程也依賴的Looper,代碼在ActivityThread中main方法中。
public static void main(String[] args) {....Looper.prepareMainLooper();ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}復制代碼
可以看到該方法執行的是Looper.prepareMainLooper方法,可看到歸根到底還是執行prepare方法
public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}復制代碼
Message又是如何引入到Handler機制的?
眾所周知,我們都知道Handler,Message,Looper是Handler機制不可或缺的要素。那么Message都是如何引入到Handler.我們看一下上述例子Message是通過handler.sendMessage(message)引入到Handler中。
public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0);}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);}復制代碼
可以看到sendMessage最終調用MessageQueue中enqueueMessage
boolean enqueueMessage(Message msg, long when) {....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.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}復制代碼
MessageQueue并沒有使用一個集合把信息保存,它只是通過使用mMessage對象表示當前需要處理消息,然后根據時間把msg進行排序。具體方法是根據時間順序調用msg.next。從而為每一個消息指定它
的下一個消息是什么。如果需要將msg作為隊頭插入到MessageQueue中可以調用sendMessageAtFrontOfQueue實現。
這樣消息就進入到MessageQueue中,那如何從MessageQueue中將消息取出來呢?
大家有沒有注意到Loop.prepare一般和Looper.loop對應使用。其實Looper.loop就是用來從MessageQueue中取出message。
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 blockif (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 loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}try {msg.target.dispatchMessage(msg);} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}....}}復制代碼
上文我們知道每個Thread都有一個Looper,其實每個Looper都對應一個MessageQueue。loop方法我們獲得對應looper中的MessageQueue不斷取出msg,并傳入到dispatchMessage.
dispatchMessage方法將取出的msg傳遞到定義Handler時重寫的handleMessage方法。
public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}復制代碼
Handler,Message,MessageQueue,Looper流程示意圖如下: