開發(fā)Android一般都用遇到使用handler的情況即硼,現(xiàn)在因為rxjava的時候可能就減少了handler的使用逃片。
使用handler需要注意內存泄漏問題(可以通過弱引用Context解決,或者在不需要使用后調用Handler.removeCallbacksAndMessages(null))只酥,當然rxjava也是會出現(xiàn)該種情況(RxLifecycle和AutoDispose都是為了不打斷rxjva鏈式調用設計的生命周期監(jiān)聽)
說到handler總是離不開looper褥实、message、messageQueue的裂允。但是使用Handler的時候也總是離不開Thread之間的使用损离。
Thread
構建Thread最終會調起init方法。在init方法中僅僅是對thread一些成員變量的初始化而已绝编,并沒有任何線程創(chuàng)建的過程僻澎。
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
Thread parent = currentThread();
if (g == null) {
g = parent.getThreadGroup();
}
//告訴ThreadGroup中就緒線程+1,但沒調用g.add所以沒添加進ThreadGroup中
g.addUnstarted();
this.group = g;
this.target = target;
this.priority = parent.getPriority();
this.daemon = parent.isDaemon();
setName(name);
init2(parent);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
tid = nextThreadID();
}
private void init2(Thread parent) {
this.contextClassLoader = parent.getContextClassLoader();
this.inheritedAccessControlContext = AccessController.getContext();
if (parent.inheritableThreadLocals != null) {
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
parent.inheritableThreadLocals);
}
}
線程的使用最后總離不開start()方法十饥。
public synchronized void start() {
// threadStatus==0表示線程未start過
if (threadStatus != 0 || started)
throw new IllegalThreadStateException();
//添加線程進ThreadGroup中窟勃,并通知start,減少就緒線程計數器
group.add(this);
started = false;
try {
//真正創(chuàng)建線程的地方
nativeCreate(this, stackSize, daemon);
started = true;
} finally {
try {
if (!started) group.threadStartFailed(this);
} catch (Throwable ignore) {
}
}
}
在native方法中去創(chuàng)建線程逗堵,最終通過linux層的pthread方式創(chuàng)建線程并調用Thread中的run的方法指針秉氧。這時候的run方法才真正在的子線程中運行。
Looper
使用looper時很簡單砸捏,短短兩句話就可以實現(xiàn)創(chuàng)建一個當前線程的Looper
Looper.prepare();
Looper.loop();
調用prepare方法時谬运,當前線程中只能有一個looper的存在。當多個時會導致RuntimeException垦藏。代碼中的ThreadLocal是什么呢梆暖?最后會稍微說下的。
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));
}
Handler的構造方法有幾個重構方法掂骏。當傳入Looper時轰驳,handler使用傳入的looper去獲取對應message。不傳入Looper時使用當前線程Looper(Looper.myLooper())。而在UI線程中早已創(chuàng)建MainLooper(在ActivityThread中的main方法级解,涉及到activity創(chuàng)建流程冒黑,這里不詳述了),所以可以直接通過空構建方法使用Handler(這時的Looper.myLooper()==Looper.getMainLooper())
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
OK勤哗,說完Looper.prepare()抡爹。這時說下Looper.loop(),該方法主要是開啟輪詢去獲取MessageQueue中的Message
public static void loop() {
final Looper me = myLooper();
//略芒划。冬竟。。民逼。
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 {
//通過Message.target(Handler)分發(fā)處理回調
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//略棚点。早处。。瘫析。
msg.recycleUnchecked();
}
}
上述代碼可以看出,Looper主要是通過MessageQueue.next()去獲取對應Message默责。獲取到Message后贬循,通過Handler.dispatchMessage方法下發(fā)處理Message。最后通過msg.recycleUnchecked回收Message桃序,供Message.obtain復用杖虾。
因為Looper.loop方法的循環(huán)等待,所以Looper.loop方法是會阻塞當前線程的媒熊。所以handler的創(chuàng)建應該房間prepare和loop方法之間奇适。在ActivityThread中也能看到調用Looper.loop()之前有一段代碼sMainThreadHandler = thread.getHandler()。其實在主線程中activity的生命周期處理及其他相關操作都是通過內部的H.class的Handler通訊實現(xiàn)的芦鳍。
MessageQueue
大概邏輯就是當有新的Message插入隊列時會喚醒隊列嚷往,若同步消息處理時間未到則再休眠指定時候后喚醒。
主要看下next方法是怎么獲取對應message的柠衅。
Message next() {
//檢查loop是否已經為退出狀態(tài)皮仁。mPrt是Native層的MessageQueue的地址。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
//如果不是第一次獲取消息,調用Native的函數贷祈,讓虛擬機刷新所有的IBinder命令趋急,確保進程在執(zhí)行可能阻塞的任務之前,釋放之前的對象势誊。
Binder.flushPendingCommands();
}
//休眠指定時間
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;
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());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//未到時間呜达,繼續(xù)休眠
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} 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();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//隊列已退出返回空,looper也相應結束
if (mQuitting) {
dispose();
return null;
}
//略粟耻。闻丑。。勋颖。嗦嗡。。
}
//略饭玲。侥祭。。茄厘。矮冬。。
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
上面看到次哈,當msg.target為空(即消息屏障)是會忽略同步消息胎署。那在系統(tǒng)中什么時候會創(chuàng)建一個taget為空的消息呢?ViewRootImpl.scheduleTraversals()方法窑滞,即是在繪圖之前會插入一個消息屏障琼牧,繪制之后移除。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//添加消息屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
Message
平時我們一般發(fā)送的Message一般為同步Message,可以通過Messasge.setAsynchronous(true)設為異步消息哀卫。
Handler.obtain和Message.obtain就簡單了巨坊,主要是Message內部維持著一個Message鏈表,獲取時先在鏈表中獲取對應緩存Message此改。Message使用完后趾撵,在Looper中通過Message.recycleUnchecked()回收
Handler
Looper的創(chuàng)建需要通過Looper.prepare()來調用,但是每個線程只能有一個Looper共啃。那是不是就意味著每次創(chuàng)建子線程Handler都需要new Thread在Thread內使用Looper的prepare和loop后再創(chuàng)建Handler呢占调?Android其實還有HandlerThread可以了解一下(不需要子線程處理后,要自行調用quit方法釋放資源喲~)移剪。但是要記住的是Looper.loop是會阻塞當前進程的究珊。
剩下Handler就只有post、sendMessage挂滓、handleMessage需要說了
post和sendMessage可以合并一塊說
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
//最終起調方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
是不是很驚喜很意外苦银,其實最終還是當成了Message處理啸胧。通過post方法傳入的Runnable最后轉為了Message.callback成員。所以Handler無論sendEmptyMessage還是post幔虏,最后還是回落到Message.obtain并對Message初始化發(fā)送的流程纺念。
但是前面MessageQueue在next方法中已經休眠了,所以在MessageQueue.enqueueMessage會根據添加Message判斷是否需要立刻喚醒隊列想括。
boolean enqueueMessage(Message msg, long when) {
//省略判斷代碼陷谱,message無依附Handler或者還在使用中直接拋出異常
synchronized (this) {
if (mQuitting) {
//隊列已結束,返回
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);//喚醒隊列
}
}
return true;
}
剩下Handler.dispatchMessage(Message msg)需要說明下Handler處理消息流程而已了。在Looper中可以看到dispatchMessage的起調是在Looper.loop方法里铺根。Looper在獲取到需要處理的Message之后宪躯,調用Message.target.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访雪,回調message.callback(所以post方法Runable會優(yōu)先執(zhí)行)
- Handler有callback,回調Handler.callback
- 最后回落handleMessage
最后在稍微說下Looper中說到的ThreadLocal
ThreadLocal
最后稍微介紹下ThreadLocal掂林。這個類就是保證每個線程只有一個Looper的關鍵臣缀。
這個類主要作用就是線程內數據共享,不同線程為不同副本泻帮。
class Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
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));
}
ThreadLocal.get方法主要是通過當前線程的ThreadLocalMap去獲取當前ThreadLocal的值精置。
class ThreadLocal
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();
}
ThreadLocalMap是ThreadLocal定義的一個內部類,以ThreadLocal為key值锣杂。
- 每個線程Thread內部有一個ThreadLocal.ThreadLocalMap類型的成員變量threadLocals(存儲實際的變量副本的脂倦,鍵值為當前ThreadLocal變量,value為變量副本(即T類型的變量))蹲堂。
- 初始時狼讨,在Thread里面,threadLocals為空柒竞,當通過ThreadLocal變量調用get()方法或者set()方法,就會對Thread類中的threadLocals進行初始化播聪,并且以當前ThreadLocal變量為鍵值朽基,以ThreadLocal要保存的副本變量為value,存到threadLocals离陶。
- 然后在當前線程里面稼虎,如果要使用副本變量,就可以通過get方法在threadLocals里面查找招刨。
所以在不同線程中調用ThreadLocal.set()實際上調用的是當前線程中的ThreadLocalMap霎俩,從而保證線程安全
而Looper只有通過靜態(tài)的Looper.prepare()方法去創(chuàng)建Looper,從而保證每個線程只有一個Looper