關於Handler的使用無非就是在線程1中發送一個message,線程2接收到到這個消息便會在線程2裏執行任務代碼。而咱們使用Handler最主要的目的就是在子線程中更新主線程的UI。因爲AndroidUI是單線程模型,因此只有在主線程中才可以去更新UI,不然系統就會崩潰拋出異常。對於Handler的使用我這裏就不在重複了,本次分析所使用的是Android8.1的源碼,和舊版源碼略有不一樣,但大體思想是同樣的,接下里就直接進入主題吧。面試
首先從Handler的構造方法開始數組
public Handler() {
this(null, false);
}
複製代碼
調用了兩個參數的構造方法。bash
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();//此處經過Looper.myLooper()獲取一個Looper
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;
}
複製代碼
在上面的註釋處,經過Looper.myLooper()去獲取一個Looper,接着判斷若是Looper爲null的話,就拋出一個異常:async
"Can't create handler inside thread that has not called Looper.prepare()"ide
該異常告訴咱們沒有調用Looper.prepare(),因此咱們在建立Handler以前必須調用該方法。 接着爲Handler的成員變量mQueue賦值,所賦的值就是咱們從獲取的Looper中取出來的。 接着咱們跳轉進Looper.myLooper()看看到底是如何獲取Looper的。oop
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
複製代碼
經過一個ThreadLocal的對象去獲取一個Looper,那這ThreadLocal是什麼呢?源碼分析
public class ThreadLocal<T>
複製代碼
ThreadLocal是一個泛型類,能夠存儲每一個線程獨有的變量。好比你在線程1中經過ThreadLocal對象的set方法存儲了一個String變量「abc」,在線程2中存儲一個String變量「def」,那麼在這兩個線程中經過ThreadLocal對象的get方法獲取該變量時會由於在不一樣的線程中獲取不一樣的值。在線程1中就會獲得「abc」,線程2中就會獲得「def」。 因而可知,經過ThreadLocal去獲取的Looper也是線程獨有的,不一樣的線程擁有不一樣的Looper。 接着咱們看看ThreadLocal的set方法。ui
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//獲取ThreadLocalMap
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
複製代碼
首先經過Thread.currentThread()獲取當前線程,接着把當前線程傳入getMap方法來獲取一個當前線程的ThreadLocalMap對象。this
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
複製代碼
getMap方法至關簡潔,就是經過返回某線程Thread上的成員變量threadLocals來獲取ThreadLocalMap的。也就是說每一個Thread內部都持有一個ThreadLocalMap對象用來存放線程間獨立的變量。 而ThreadLocalMap其實就是ThreadLocal的一個靜態內部類。在ThreadLocalMap的內部有一個數組。spa
private Entry[] table;
複製代碼
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
複製代碼
一個弱引用對象的數組。每一個Entry對象持有一個ThreadLocal的弱引用,而值存放在value字段上。因此ThreadLocalMap存放了當前線程的獨立變量,他們全都放在table字段中。
接着上面的 代碼,若是map不爲null的話就把值設置在這個ThreadLocalMap對象裏 咱們來看看ThreadLocalMap的set方法。
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
複製代碼
根據ThreadLocal的hash值和數組的長度作與運算獲得的下標來做爲value存放的位置。 若是下標所處的位置不爲空,那說明當前下標不能夠存放value,那就調用nextIndex來取得下一個下標,若是下一個下標所處的位置是null,那麼就能夠把value存放在當前下標位置。大體邏輯就是這樣的。 接下來咱們再看看ThreadLocal的get方法。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
複製代碼
首先經過getMap獲取到ThreadLocalMap,接着經過ThreadLocalMap.getEntry這個方法獲取到存放在ThreadLocal當中的值。 到此咱們能夠作一下總結:
在每一個Thread的內部都有一個ThreadLocalMap,這個ThreadLocalMap裏有一個名爲table的Entry數組,因此ThreadLocal裏存放的變量才獨立於每一個線程。咱們往ThreadLocal裏存放的對象都是存放進這個Entry數組的(經過hash計算來決定下標值)。 因此把Looper存放在ThreadLocal當中就能夠保證每一個線程的Looper是獨立存在的。
當Handler獲取了Looper之後就能夠正常工做了。咱們使用Handler時通常會調用Handler的sendMessage方法
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
複製代碼
咱們跳進sendMessageDelayed,發現最終會調用
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);
}
複製代碼
這裏的mQueue就是咱們構造方法裏Looper的mQueue,因此Looper是必須的。 看看最後一行enqueueMessage。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
在這裏msg.targe指向了this,也就是咱們當前的Handler。 發現最後是調用的MessageQueue的enqueueMessage,咱們繼續跟蹤。
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) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
複製代碼
說是消息隊列,其實MessageQueue是個鏈表,由於對於鏈表來講插入和刪除操做的時間複雜度是O(1),因此很是適合用來作消息隊列,在enqueueMessage方法裏把新消息插入隊尾,也就是下面這兩行代碼。
msg.next = p;
prev.next = msg;
複製代碼
咱們經過sendMessage方法把一個Message放進消息隊列中,那麼是誰來處理消息隊列中的消息呢。那就須要Looper登場了,前面說過一個線程若是須要建立一個Handler那麼就必需要有一個Looper。咱們來看看Looper的構造方法。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製代碼
在Looper的構造方法裏會新建一個MessageQueue,因此這個MessageQueue和Looper是綁定了的,同時會獲取建立Looper的那個線程。要使用Looper還必需要調用Looper.loop()方法。
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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // 從消息隊列中獲取消息
if (msg == null) {
return;
}
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);
}
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();
}
}
複製代碼
調用loop方法後會啓動一個無限循環,每次循環都會調用以下方法。從消息隊列中獲取一個Message,若是消息隊列中沒有消息能夠取了,該方法可能會阻塞。
Message msg = queue.next();
複製代碼
接着會調用
msg.target.dispatchMessage(msg);
複製代碼
這個target就是以前enqueueMessage時設置的handler,經過調用handler的dispatchMessage執行咱們的任務。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
若是建立Message時傳入了callback那就調用handleCallback執行任務,不然查看mCallback是否能夠調用,能夠則調用。mCallback是繼承Handler時可選的傳入參數。
到此一個完整的Handler機制原理就講解完畢了。
前面說過一個線程中必需要有一個Looper才能使用Handler,不然就會奔潰。那爲何咱們在主線程中可使用Handler呢?這是由於主線程ActivityThread已經幫咱們作好了Looper的初始化工做。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
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();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼
使用了Looper.prepareMainLooper();
來初始化Looper,並使用了Looper.loop()
開啓了消息循環隊列。 還有一點須要注意的是,在使用Handler的時候,若是消息都處理完畢了,應該調用Loop.quit或者Looper.quitSafely方法來中止輪詢。不然因爲Looper會不斷的輪詢MessageQueue致使該Looper所在的線程沒法被釋放。
Handler在近幾年來已經算是很是基礎的知識了,在面試當中也是高頻題,關於Handler的使用實際上是很容易形成內存泄漏的,這裏就不在多說了,有興趣的朋友能夠搜索一下Handler內存泄漏相關的知識。