經過Handler能夠輕鬆的將一個任務切換到Handler所在的線程中執行.
應用:在Android開發中,有時候須要在子線程中進行耗時的IO操做(讀取文件或訪問網絡),當 耗時操做完成之後可能須要再UI上作一些改變,因爲Android開發規範的限制,咱們並不能在 子線程中訪問UI控件,不然就會出發程序異常,此時經過Handler就能夠將更新UI的操做切換到主線程中執行.java
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).數組
官方的介紹是:該類提供線程局部變量.這些變量不一樣於它們的正常變量,即每個線程訪問自身的局部變量時,都有它本身的,獨立初始化的副本.該變量一般是與線程關聯的私有靜態字段,列如用於ID或事物ID.
經過ThreadLocal,每一個線程均可以獲取本身線程內部的私有變量.網絡
ThreadLocal<String> mThreadLocal = new ThreadLocal<>(); //建立了一個ThreadLocal的對象
mThreadLocal.set("這是一個測試文案"); //設置值
mThreadLocal.get(); //獲取值
複製代碼
當咱們建立了ThreadLocal對象後就能夠調用set()/get()
方法來進行數據的賦值與訪問了.async
public void set(T value) {
Thread t = Thread.currentThread(); //獲取當前線程
ThreadLocalMap map = getMap(t); //拿到線程的ThreadLocalMap
if (map != null) //對ThreadLocalMap作驗證,存在就直接賦值,不然建立並賦值
map.set(this, value); //賦值 this(key 當前ThreadLocal對象) value(所賦的值)
else
createMap(t, value); //建立ThreadLocalMap並賦值
}
複製代碼
//獲取ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//建立ThreadLocalMap並賦值
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
複製代碼
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //拿到線程中的ThreadLocalMap
if (map != null) { //若是ThreadLocalMap爲null,建立新的ThreadLocalMap
ThreadLocalMap.Entry e = map.getEntry(this); //根據key值拿到Entry對象
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value; //獲取存儲的數據
return result;
}
}
return setInitialValue();
}
複製代碼
咱們能夠看到,在set()/get()
方法中,咱們首先拿到當前線程,並經過當前線程去獲取到對應的ThreadLocalMap,而後經過再調用的ThreadLocalMap的set()/get()
方法.因此其實本質上來說,ThreadLocal是經過ThreadLocalMap來進行的數據的操做.ide
ThreadLocalMap是ThreadLocal中的一個靜態內部類,是一個定製散列映射,只用於維護線程私有值.oop
static class ThreadLocalMap {
//存儲的數據爲Entry且key爲弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//初始容量
private static final int INITIAL_CAPACITY = 16;
//用於存儲數據
private Entry[] table;
//用於數組擴容 默認爲數組長度的2/3
private int threshold;
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
//第一次放入數據,初始化數組長度,定義擴容因子大小.
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY]; //初始化數組長度爲16
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//根據哈希值計算位置
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY); //設置擴容因子爲當前數組的2/3
}
}
複製代碼
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(); //獲取數據的key
//若是key值相同,則直接覆蓋數據
if (k == key) {
e.value = value;
return;
}
//若是key值爲null,則清空全部key爲null的數據
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//若是上述條件都不知足,則直接添加數據
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
複製代碼
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1); //根據key值拿到位置
Entry e = table[i]; //根據位置拿到數據
if (e != null && e.get() == key) //判斷是否有數據,成功則直接返回
return e;
else //
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key) //key值相同,直接返回
return e;
if (k == null) //key爲null,清楚當前位置下全部key爲null的數據
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null; //沒有數據返回null
}
複製代碼
綜上所述,不管調用set()
仍是get()
方法,都會清楚key值爲null的數據.post
/** * Returns a reference to the currently executing thread object. * * @return the currently executing thread. */
@FastNative
public static native Thread currentThread();
複製代碼
由於Thread t = Thread.currentThread();
時可能會獲取不到當前線程,也就沒法拿到對應的ThreadLocalMap.測試
若是key使用了強引用,當引用ThreadLocal的對象被回收了,但ThreadLocalMap中還持有ThreadLocal的強引用,若是沒有手動清除,則會致使內存泄漏.ui
由於使用的弱引用,GC的時候ThreadLocal則會被回收,那麼此時ThreadLocalMap中就會出現key爲null的Entry,也就沒有辦法訪問這些Entry的value了.
若是當前線程遲遲不結束,這些key爲null的value就會一存在一條強引用鏈:Thread Ref(當前線程引用) -> Thread -> ThreadLocalMap -> Entry -> value
,那麼就會致使這些Entry永遠沒法回收,形成內存泄漏.this
使用static修飾的ThreadLocal可能致使內存泄漏(Java虛擬機在加載類的過程當中爲靜態變量分配內存,static變量的生命週期取決於類的生命週期,也就是說類被卸載時,靜態變量纔會被銷燬並釋放內存空間,而類的聲明週期與下面的三個條件相關)
.
Handler mHandler = new Handler();
複製代碼
//無參構造方法調用有參構造
public Handler() {
this(null, false);
}
//有參構造
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(); //經過ThreadLocal獲取Looper對象
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;
}
複製代碼
若是當前線程中不存在Looper實例則會觸發異常,此時須要執行Looper.prepare()
方法來初始化Looper實例.
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //若是已經有Looper實例,則會拋出異常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed)); //建立Looper對象實例
}
複製代碼
執行Looper.prepare()
方法時會檢測當前線程中是否已經存在Looper對象,已經存在則會觸發異常,每一個線程中只容許有一個Looper對象實例存在,若是不存在就建立一個.
private Looper(boolean quitAllowed) { //Looper的構造方法
mQueue = new MessageQueue(quitAllowed); //建立MessageQueue對象實例
mThread = Thread.currentThread(); //將mThread設置爲當前線程
}
複製代碼
ActivityThread.java public static void main(String[] args) {
...
Looper.prepareMainLooper(); //初始化Looper對象
...
}
複製代碼
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
複製代碼
在ActivityThread的main()
方法中執行了Looper.prepareMainLooper()
方法,此方法中又調用了Looper.prepare()
方法完成了Looper對象的初始化.
在子線程中建立Handler時,由於沒有Looper對象,因此須要咱們主動調用Looper.prepare()
方法進行初始化.
Message msg = new Message(); //構造Message對象
//爲msg添加須要攜帶的數據
msg.setData(Bundle data);
msg.obj = xxx;
msg.what = xxx ;
複製代碼
主要發送方法:
sendMessageAtFromOfQueue()
方法以外,其餘的方法最後都會調用到sendMessageAtTime()
方法中.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);
}
複製代碼
該方法接收兩個參數,一個是咱們發送的Message對象,另外一個表示發送的時間(值等於自系統開機到當前時間的毫秒數再加上延遲時間).
Handler.java private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
MessageQueue.java 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;
}
複製代碼
入隊,使用mMessage對象保存當前待處理的消息,入隊時按照時間將全部的消息排序,使用msg.next來指定下一個消息對象,而後將這條消息的next指定爲剛纔的mMessage對象,此過程爲添加消息到消息隊列的頭部.
public static void loop() {
final Looper me = myLooper(); //拿到當前的looper
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();
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) { //輪詢遍歷
Message msg = queue.next(); //判斷當前MessageQueue中是否存在mMessage(待處理消息),若是存在就出隊,而後下一條消息做爲mMessage,不然就處於阻塞的狀態,一直等到有新的消息入隊.
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 traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg); //拿到Handler對象,調用處理消息分發的方法
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
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();
}
}
複製代碼
MessageQueue.next
方法判斷當前MessageQueue中是否存在mMessage(待處理消息),若是存在就出隊,而後下一條消息做爲mMessage,不然就處於阻塞的狀態,一直等到有新的消息入隊.
public void dispatchMessage(Message msg) {
//根據設置的回調判斷不一樣的處理方法
if (msg.callback != null) { //若是msg設置了Runnable則調用該方法
handleCallback(msg);
} else { //處理數據的回調
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
最後在咱們重寫的handleMessage()
方法中處理咱們所要作的業務操做.