參考文獻(xiàn):
- Android異步消息處理機(jī)制源碼剖析
- Handler全家桶之 —— Handler 源碼解析
- 你真的懂Handler嗎案站?Handler問答
- android的消息處理機(jī)制(圖+源碼分析)——Looper,Handler,Message
1 概述
?主線程不能執(zhí)行耗時(shí)操作邓馒,因?yàn)闀枞谧泳€程里進(jìn)行耗時(shí)操作勋拟;子線程不能更新UI法梯,用handler發(fā)送一個(gè)更新UI的消息苔货,handler分發(fā)消息,處理消息立哑。
子線程為何不能訪問UI?
- 源碼角度:當(dāng)訪問UI時(shí)夜惭,ViewRootImpl會調(diào)用checkThread()方法檢查當(dāng)前線程是哪個(gè)線程,如果不是UI線程會拋出異常;
- 線程安全角度:訪問UI不是線程安全的铛绰;
- 訪問UI為什么不加鎖:邏輯復(fù)雜诈茧、效率低;
?Handler作用:
- 線程間通信(例如捂掰,子線程通知主線程更新UI)敢会;
- 執(zhí)行計(jì)劃任務(wù);
?下面源碼分析基于Android 8.0镊叁;
2 Message 消息
?Message消息,是多線程間通信的實(shí)體走触,是Handler發(fā)送和處理的對象晦譬。Message對象實(shí)現(xiàn)了Parcelable接口,說明Message對象支持序列化/反序列化操作互广。
2.1 屬性
//msg ID
public int what;
//存儲int類型的數(shù)據(jù)域
public int arg1;
//存儲int類型的數(shù)據(jù)域
public int arg2;
//存儲Object類型數(shù)據(jù)域
public Object obj;
//存儲Bundle類型數(shù)據(jù)域
/*package*/ Bundle data;
/*package*/ static final int FLAG_IN_USE = 1 << 0;
//消息標(biāo)識敛腌,當(dāng)消息對象進(jìn)入消息隊(duì)列或回收時(shí)設(shè)置為FLAG_IN_USE,msg.obtain時(shí)設(shè)置為0
/*package*/ int flags;
//處理消息的時(shí)間
/*package*/ long when;
//發(fā)送和處理消息的Handler
/*package*/ Handler target;
//post的Runnable
/*package*/ Runnable callback;
// 鏈?zhǔn)浇Y(jié)構(gòu)惫皱,指向下一個(gè)Message對象像樊,用于維護(hù)鏈表結(jié)構(gòu)的消息池(消息隊(duì)列)
/*package*/ Message next;
//信號量,消息池的加鎖對象
private static final Object sPoolSync = new Object();
//消息池的表頭旅敷,由它維護(hù)了一個(gè)鏈?zhǔn)较⒊厣鳎?dāng)消息被回收的時(shí)候,會加入到這個(gè)消息池中
private static Message sPool;
//消息池大小
private static int sPoolSize = 0;
//消息池最大容量50媳谁,消息隊(duì)列的最大容量是50
private static final int MAX_POOL_SIZE = 50;
-
Message
可傳輸int , Object ,Bundle類型的數(shù)據(jù)涂滴; - 如果你的message只需要攜帶簡單的int,請優(yōu)先使用
Message.arg1
和Message.arg2
來傳遞信息晴音,這比用Bundle
更省內(nèi)存柔纵; - 擅用
message.what
來標(biāo)識信息,以便用不同方式處理message; -
Message
維護(hù)了一個(gè)全局的消息池(消息隊(duì)列)锤躁,消息隊(duì)列最大容量是50搁料;消息被回收后,會放入到消息池中系羞,并將flag
字段設(shè)置為FLAG_IN_USE
;
2.2 靜態(tài)obtain()
方法
public static Message obtain() {
synchronized (sPoolSync) {//對消息池加鎖
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // flags設(shè)為0
sPoolSize--;//從鏈表刪除
return m;
}
}
return new Message();//若消息池為空郭计,直接new
}
?obtain
方法用于獲取一個(gè)消息對象,如果當(dāng)前消息池為空椒振,直接new昭伸,否則從消息池頭部取一個(gè)消息對象進(jìn)行復(fù)用;
obtain
方法還有好幾個(gè)重載方法杠人,但最終都會調(diào)用該該無參方法勋乾。
2.3 recycle()
方法
//可手動回收消息
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
//真正回收Message的方法,looper.loop()在從消息隊(duì)列取出并處理消息后調(diào)用這個(gè)方法
void recycleUnchecked() {
flags = FLAG_IN_USE; //修改標(biāo)記嗡善?辑莫?
//為了無差別(handler發(fā)送的所有消息)復(fù)用消息對象,清空所有域
what = 0; arg1 = 0; arg2 = 0;
obj = null; replyTo = null; sendingUid = -1;
when = 0;target = null;callback = null;data = null;
synchronized (sPoolSync) {//將使用完的消息回收后放入消息池罩引,頭插法
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
?在Message對象被處理或從消息隊(duì)列移除后各吨,可以手動調(diào)用recycle()
方法回收消息對象;當(dāng)然recycleUnchecked()
方法才是真正回收消息的方法,looper.loop()
在從消息隊(duì)列取出并處理消息后調(diào)用這個(gè)方法進(jìn)行消息回收揭蜒;這個(gè)方法首先會將flag標(biāo)記為FLAG_IN_USE横浑,并把清空所有屬性;并在消息池沒有達(dá)到最大限定值的情況下屉更,把這個(gè)對象插入消息池的表頭徙融。同樣,在操作消息池的時(shí)候需要先對sPoolSync信號量加鎖瑰谜。
3 MessageQueue消息隊(duì)列
?MessageQueue
是一個(gè)常量類欺冀,不允許被繼承;
?消息隊(duì)列用來存放Handler發(fā)送過來的消息萨脑,內(nèi)部通過單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護(hù)消息列表隐轩,等待Looper的抽取。
3.1 消息出隊(duì)next()
Message next() {
//...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
//死循環(huán)從隊(duì)列取Message渤早,直到返回一個(gè)Message职车,或者M(jìn)essageQueue退出
for (;;) {
//...
synchronized (this) {//消息隊(duì)列加鎖
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//取隊(duì)頭消息,若該消息不為空且者是屏障消息(target為空)鹊杖,則繼續(xù)遍歷悴灵,直到取到一個(gè)異步消息為止
//屏障消息:target為空時(shí)是屏障消息;用于區(qū)分同步消息和異步消息仅淑;如果設(shè)置了屏障消息称勋,只執(zhí)行異步消息,不執(zhí)行同步消息涯竟,直到移除了屏障;如果沒設(shè)置屏障消息空厌,同步消息和異步消息都執(zhí)行
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) {//如果消息執(zhí)行時(shí)間未到庐船,繼續(xù)循環(huán),等待時(shí)間到
// 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);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
//preMsg不空嘲更,說明此時(shí)隊(duì)列頭結(jié)點(diǎn)是一個(gè)target為空的屏障消息筐钟,同時(shí)msg此時(shí)是異步消息。
prevMsg.next = msg.next;//直接從鏈表取下該消息
} else {//此時(shí)msg是隊(duì)列頭結(jié)點(diǎn)赋朦,直接刪除隊(duì)頭即可
mMessages = msg.next;
}
msg.next = null;//斷開next鏈接
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();//修改標(biāo)記
return msg;//取道非空消息退出
}
} else {//隊(duì)列為空篓冲,next方法阻塞,繼續(xù)循環(huán)宠哄,等待新消息到來
// No more messages.
nextPollTimeoutMillis = -1;
}
//若消息隊(duì)列已退出壹将,返回true退出死循環(huán)
if (mQuitting) {
dispose();
return null; //返回null后Looper.loop()方法也會結(jié)束循環(huán)
}
//...
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;//當(dāng)隊(duì)列為空時(shí),next()方法會阻塞毛嫉,繼續(xù)循環(huán)诽俯,直到有新消息到達(dá)隊(duì)列
continue;
}
//...
}
}
-
next()
方法用于將隊(duì)列頭部消息出隊(duì)并返回; - 該方法內(nèi)部有個(gè)死循環(huán)承粤,如果消息隊(duì)列中沒消息暴区,next方法會阻塞闯团,繼續(xù)循環(huán),直到取道新消息仙粱;如果消息隊(duì)列中有消息房交,先判斷執(zhí)行時(shí)間是否到了,如果時(shí)間沒到則等待伐割,繼續(xù)循環(huán)候味;如果時(shí)間到了就將消息出隊(duì)返回;
- 在循環(huán)過程中會對消息隊(duì)列加鎖口猜,所以該方法是線程安全的负溪;
3.2 消息入隊(duì)enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//入隊(duì)的消息的target,必須不為空济炎,否則會拋異常
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {//消息在回收之前川抡,不可以被重復(fù)使用
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {//如果消息隊(duì)列在退出狀態(tài) ,則直接回收消息须尚,返回false
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;
}
//把消息標(biāo)記為在使用狀態(tài)崖堤,設(shè)置when
msg.markInUse();
msg.when = when;
Message p = mMessages;//此時(shí)p是鏈表頭部
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//如果隊(duì)列為空或者when等于0,或者when小于隊(duì)頭Message的when耐床,則直接把消息插入隊(duì)頭
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;//prev是p的前驅(qū)節(jié)點(diǎn)密幔,依次遍歷
p = p.next;
if (p == null || when < p.when) {
break;//當(dāng)p已經(jīng)到隊(duì)尾或者找到一個(gè)節(jié)點(diǎn)msg.when < p.when時(shí)退出循環(huán)
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//鏈表插入操作,把msg插入到p節(jié)點(diǎn)前邊撩轰,并把p的前驅(qū)節(jié)點(diǎn)的next改為msg
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
//...
}
return true;//插入成功胯甩,返回true
}
- 該方法用于將消息入隊(duì),其實(shí)就是單鏈表的插入操作;
- 該方法對鏈表隊(duì)列操作時(shí)堪嫂,依然是進(jìn)行了加鎖同步偎箫,所以是線程安全的;
- 隊(duì)列是一個(gè)(消息執(zhí)行時(shí)間)when升序鏈表皆串,所以插入也必須找到合適的節(jié)點(diǎn)進(jìn)行插入淹办;如果待插入Message不設(shè)置when或when=0,直接插入隊(duì)列頭部恶复;否則遍歷隊(duì)列結(jié)點(diǎn)怜森,直到找到第一個(gè)大于when的結(jié)點(diǎn),插入到該結(jié)點(diǎn)的前面谤牡;
4 Looper消息泵
?通過Looper.loop()
不斷地從MessageQueue中抽取Message副硅,將消息分發(fā)給目標(biāo)處理者(Handler);
4.1 主要屬性和構(gòu)造器
//線程本地變量拓哟,每個(gè)線程有一個(gè)獨(dú)立的Looper對象想许,不存在線程安全問題
//如果不調(diào)用prepare()方法,sThreadLocal.get()返回null
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // 主線程的Looper,由Looper.class維護(hù)
final MessageQueue mQueue;//looper的MessageQueue
final Thread mThread;//(創(chuàng)建)Looper線程
//私有構(gòu)造器流纹,不允許外部調(diào)用
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- Looper的構(gòu)造器是私有的糜烹,不能在Looper類外部new Looper,所以在Looper類外部必須調(diào)用prepare()方法來創(chuàng)建一個(gè)Looper對象漱凝;
- Looper內(nèi)部有一個(gè)MessageQueue屬性疮蹦;
4.2 創(chuàng)建Looper
?用Looper.prepare()
創(chuàng)建Looper,而不是new茸炒;
//公有方法愕乎,開發(fā)者只能調(diào)用這個(gè)方法為當(dāng)前線程創(chuàng)建Looper,允許退出
public static void prepare() {
prepare(true);
}
//私有方法壁公,開發(fā)者無法調(diào)用感论,同一個(gè)線程只允許調(diào)用一次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));//將Looper對象設(shè)置為線程本地變量
}
//主線程的Looper初始化紊册,雖然是公有方法比肄,我們無法調(diào)用,
//因?yàn)橄到y(tǒng)啟動的時(shí)候已經(jīng)調(diào)用過了囊陡,如果再次調(diào)用芳绩,會拋異常
public static void prepareMainLooper() {
prepare(false);
synchronized(Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//把得到的主線程Looper賦值給sMainLooper
sMainLooper = myLooper();
}
}
//獲取當(dāng)前線程的Looper對象
public static@Nullable Looper myLooper() {
return sThreadLocal.get();//獲取線程本地變量
}
- 調(diào)用
Looper.prepare()
為當(dāng)前線程創(chuàng)建Looper對象,并將Looper對象設(shè)置為線程本地變量撞反; - 調(diào)用
Looper.myLooper()
方法來獲取當(dāng)前線程的Looper對象; - 主線程的Looper允許MessageQueue退出妥色,而其他線程不允許;
- 一個(gè)線程只能調(diào)用一次
Looper.prepare()
方法遏片,否則會拋出異常嘹害,所以在prepare()
創(chuàng)建Looper對象之前,應(yīng)該先調(diào)用Looper.myLooper()
方法判斷是否為空吮便;同時(shí)也說明了一個(gè)線程只有一個(gè)Looper對象吼拥; - 主線程的Looper在ActivityThread中的main()方法中創(chuàng)建的,所以主線程不需要手動創(chuàng)建Looper线衫;
//主線程中不需要自己創(chuàng)建Looper
public static void main(String[] args) {
//...
Looper.prepareMainLooper();//為主線程創(chuàng)建Looper,該方法內(nèi)部又調(diào)用 Looper.prepare()
//...
Looper.loop();//開啟消息輪詢
//...
}
- 另外可以在任何地方調(diào)用Looper.getMainLooper();獲取主線程的Looper惑折;
?但是子線程就不一樣了授账,子線程在創(chuàng)建Handler對象前必須手動調(diào)用Looper.prepare()
方法創(chuàng)建Looper對象;
//子線程中創(chuàng)建Looper的標(biāo)準(zhǔn)寫法
new Thread(new Runnable() {
@Override
public void run() {
if(Looper.myLooper()==null){//保證一個(gè)線程只有一個(gè)Looper
Looper.prepare();//創(chuàng)建Looper
}
Handler handler=new Handler();
Looper.loop();//開啟消息輪詢
}
}).start();
4.3 開啟消息輪詢loop()
//代碼省去打印等其他無關(guān)邏輯
public static void loop() {
//獲取當(dāng)前線程的sThreadLocal變量惨驶,即Looper對象
final Looper me = myLooper();
//如果當(dāng)前線程沒有調(diào)用過prepare()方法白热,則me為null,拋出異常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//從me里獲得本線程的MessageQueue對象
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//開啟消息輪詢
for (;;) {
//從消息隊(duì)列取消息
Message msg = queue.next(); // 當(dāng)消息隊(duì)列為空且未退出時(shí)粗卜,next方法會阻塞
if (msg == null) {
//next返回null表明消息隊(duì)列已退出
return;//結(jié)束輪詢屋确,loop方法唯一出口
}
try {
//target是Message的Handler,調(diào)用它的dispatchMessage()方法來分發(fā)消息
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
final long newIdent = Binder.clearCallingIdentity();
msg.recycleUnchecked();//當(dāng)消息被處理完后,回收當(dāng)前消息
}
}
- 通過調(diào)用
Looper.loop()
方法開啟消息輪詢攻臀;該方法會調(diào)用queue.next()
方法從隊(duì)列中取頭部消息焕数; - 該方法會循環(huán)調(diào)用
msg.target.dispatchMessage(msg);
來進(jìn)行消息分發(fā),分發(fā)給消息的target刨啸,也就是消息對應(yīng)的的Handler對象堡赔; - 當(dāng)消息被處理完后,會調(diào)用
msg.recycleUnchecked();
回收消息设联,當(dāng)前消息放入消息池善已,以便以后復(fù)用; - handler是在它關(guān)聯(lián)的looper線程(創(chuàng)建Looper對象的線程)中處理消息的离例;
5 Handler 消息處理器
5.1 主要屬性和構(gòu)造器
5.1.1 主要屬性
//是否發(fā)現(xiàn)潛在的內(nèi)存泄漏换团,默認(rèn)為false
private static final boolean FIND_POTENTIAL_LEAKS = false;
private static final String TAG = "Handler";
//靜態(tài)全局變量,主線程的Handler
private static Handler MAIN_THREAD_HANDLER = null;
//綁定的Looper對象
final Looper mLooper;
//綁定的MessageQueue消息隊(duì)列宫蛆,通過looper獲取
final MessageQueue mQueue;
//回調(diào)接口
final Callback mCallback;
//是否是異步的艘包,如果是異步的,在發(fā)送消息的時(shí)候洒扎,
//會調(diào)用Message.setAsynchronous(true)把消息設(shè)為異步消息
final boolean mAsynchronous;
- 由此可見一個(gè)Handler持有一個(gè)Looper類型的屬性辑甜;即一個(gè)Handler對應(yīng)一個(gè)唯一的Looper;而對應(yīng)的MessageQueue消息隊(duì)列袍冷,通過Looper屬性獲攘状住;
5.1.2 構(gòu)造器
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
//如果為true胡诗,如果Handler實(shí)現(xiàn)類是匿名類或內(nèi)部類或非static類邓线,會給出警告,告知開發(fā)者存在內(nèi)存泄漏的風(fēng)險(xiǎn)
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());
}
}
//獲取當(dāng)前線程的線程本地變量煌恢,即Looper對象骇陈;
mLooper = Looper.myLooper();
if (mLooper == null) {//創(chuàng)建Handler對象前要調(diào)用Looper.prepare
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//mQueue直接拿Looper里的MessageQueue類型的引用對象
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//該構(gòu)造器可為當(dāng)前Handler指定Looper對象,所以Handler對象和Looper對象不一定是在同一線程創(chuàng)建的
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler() {this(null, false);}
public Handler(Callback callback) {this(callback, false);}
public Handler(Looper looper) {this(looper, null, false);}
Handler對應(yīng)的MessageQueue對象來自其關(guān)聯(lián)的Looper對象瑰抵;
其他構(gòu)造方法都會調(diào)用前面?zhèn)z構(gòu)造器中一個(gè)你雌;早常用的構(gòu)造器是
Handler()
和Handler(Callback)
;Handler(Looper looper)
用于為當(dāng)前Handler指定關(guān)聯(lián)的Looper對象;Handler關(guān)聯(lián)的Looper對象既可以來自當(dāng)前線程(創(chuàng)建Handler實(shí)例的線程)的本地變量二汛,也可以在構(gòu)造器里指定婿崭;前面一種情況,Handler和Looper是在同一線程里實(shí)例化的肴颊,后面一種情況不一定氓栈;
5.1.3obtainMessage
public final Message obtainMessage() {
return Message.obtain(this);
}
?使用該方法獲取handler當(dāng)前處理的Message;Handler中有一系列obtanMessage()重載方法婿着,最后調(diào)用的還是該方法授瘦;需要注意的是醋界,在Message中,obtain()方法是靜態(tài)方法提完,在Handler中形纺,是非靜態(tài)的,需要通過具體的Handler實(shí)例對象來獲得氯葬,但是禁止子類進(jìn)行覆寫挡篓;
5.2 發(fā)送消息
?在Handler中,可以發(fā)送一個(gè)Runnable對象帚称,也可以發(fā)送一個(gè)Message對象官研;通過sendMessage(Message)
方式發(fā)送一個(gè)Message對象;通過post(Runnable)
方式發(fā)送一個(gè)Runnable對象闯睹,這個(gè)Runnable對象最終也會被包裝成一個(gè)Message對象發(fā)送戏羽;
5.2.1 post
方式
//立即post一個(gè)Runnable對象到MessageQueue中,此時(shí)Runnable對象被包裝成Message后入隊(duì)(when == 當(dāng)前系統(tǒng)時(shí)間楼吃,可能是隊(duì)頭始花,也可能不是隊(duì)頭,隊(duì)列中已經(jīng)有when值小于當(dāng)前時(shí)間的Message)
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
//Runnable包裝成Message后加入到MessageQueue中孩锡,但此時(shí)
//Message.when=uptimeMillis酷宵,uptimeMills是消息的執(zhí)行時(shí)間,
public final boolean postAtTime(Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
//同上躬窜,只是又傳入了token對象浇垦,存儲在Message.obj中
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
//Runnable包裝成Message后加入到MessageQueue中,
//但是Message.when = now + delayMillis荣挨,
//表示延遲delayMills后執(zhí)行
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
//Runnable包裝成Message后加入到MessageQueue中男韧,此時(shí)when=0,所以一定是在MessageQueue的隊(duì)頭
public final boolean postAtFrontOfQueue(Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
//把Runnable對象包裝成Message對象默垄,可見只是把Runnable對象賦值給了Message的callback域
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
//上述方法的重載方法此虑,把token賦值給了Message的obj域,可以用這個(gè)方法進(jìn)行傳Object數(shù)據(jù)
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
5.2.2 sendMessage
方式
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
//延遲發(fā)送口锭,把當(dāng)前時(shí)間加上延遲時(shí)間后調(diào)用了sendMessageAtTime()方法
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//發(fā)送消息的方法朦前,對queue判空后,調(diào)用enqueueMessage進(jìn)行實(shí)際入隊(duì)
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);
}
//實(shí)際對消息入隊(duì)的方法鹃操,在該方法中况既,會把Message的target域進(jìn)行賦值,
//如果mAsynchronous是true组民,則會調(diào)用setter方法把消息設(shè)置為異步消息,
//調(diào)用的入隊(duì)方法其實(shí)是調(diào)用的MessageQueue的enqueueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
//...
//省略其他方法悲靴,基本上跟post系列方法是一一對應(yīng)的
- 幾個(gè)時(shí)間關(guān)系:
when = now + delay
,when 表示分發(fā)消息(dispatchMessage
)的時(shí)間臭胜,now表示當(dāng)前(相對系統(tǒng)啟動)時(shí)間SystemClock.uptimeMillis()
莫其,delay表示延遲時(shí)間delayMillis
;
?post(runnable)
方式和sendMessage(msg)
方式發(fā)送消息的聯(lián)系和區(qū)別:
- 聯(lián)系:調(diào)用鏈都是
handler.sendMessageAtTime()
->messageQueue.enqueueMessage()
將Message發(fā)送到消息隊(duì)列; - 區(qū)別:
- 消息內(nèi)容不同:
sendMessage
發(fā)送的消息側(cè)重于傳數(shù)據(jù)耸三,而handleCallback
側(cè)重于傳任務(wù)(Runnable)乱陡; - 處理消息的方式不同:一般情況,
sendMessage
發(fā)送的消息最終會調(diào)用handler或callback的handleMessage
方法來處理仪壮;而post(runnable)
發(fā)送的消息最終會調(diào)用handler.handleCallback
方法來處理憨颠;
- 消息內(nèi)容不同:
5.3 處理消息
//消息分發(fā)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//post的Runnable參數(shù)
handleCallback(msg);
} else {
if (mCallback != null) {//Handler(Callback)構(gòu)造器,Handler無需派生子類
if (mCallback.handleMessage(msg)) {//一般為true积锅,若為false爽彤,還要執(zhí)行handleMessage
return;
}
}
handleMessage(msg);//優(yōu)先級最低
}
}
private static void handleCallback(Message message) {//處理消息方式1,優(yōu)先級最高
message.callback.run();//執(zhí)行post的Runnable參數(shù)地run回調(diào)方法缚陷,因此Runnable run里可以有一些更新UI的操作
}
public interface Callback {//優(yōu)先級次之
public boolean handleMessage(Message msg);//處理消息方式2适篙,在調(diào)用Handler(Callback)構(gòu)造器實(shí)例化Handler時(shí)實(shí)現(xiàn)該方法
}
//Handler子類必須實(shí)現(xiàn)這個(gè)空方法來接收消息
public void handleMessage(Message msg) {//處理消息方式3,優(yōu)先級最低
}
- 有兩類發(fā)送消息的方式:
sendMessage(msg)
方式和post(Runnable r)
方式箫爷; -
處理消息方式有三種:
handleCallback
方式嚷节、callback.handleMessage
方式、handler.handleMessage
方式虎锚;并且優(yōu)先級遞減硫痰;
6 四要素之間的關(guān)系
6.1 四要素之間的關(guān)系
- 一個(gè)線程中可創(chuàng)建多個(gè)Handler對象;但是一個(gè)線程中只能創(chuàng)建一個(gè)Looper對象(因?yàn)橐粋€(gè)線程中只能調(diào)用一次
Looper.preapre()
窜护,否則會報(bào)異常)效斑; - 一個(gè)Looper對象可以對應(yīng)多個(gè)線程,比如主線程的mainLooper柄慰,供主線程和所屬子線程(
Looper.getMainLooper()
)共同使用鳍悠; - Looper類中有一個(gè)
final MessageQueue mQueue
屬性; - Handler類中有一個(gè)屬性
final Looper mLooper
屬性,Handler關(guān)聯(lián)的消息隊(duì)列通過Looper獲茸Α藏研; - 一個(gè)消息隊(duì)列中有多個(gè)Message對象,不同消息的target可以不同概行,所以消息隊(duì)列中的消息可以來自不同的Handler對象;
- Message類有一個(gè)
Handler target
屬性蠢挡,這是消息對象關(guān)聯(lián)的Handler對象;
6.2 異步消息處理機(jī)制的原理(圖非常重要)
?以下面應(yīng)用場景為例:在主線程里實(shí)例化Looper和Handler凳忙;在子線程(工作線程)處理耗時(shí)任務(wù)业踏,由于要將任務(wù)執(zhí)行結(jié)果在UI上展示,需要更新UI涧卵;在子線程中創(chuàng)建一個(gè)更新UI的Message對象勤家,并使用Handler對象的引用發(fā)送該消息;最后在主線程里處理消息柳恐,更新UI伐脖;下面是sample代碼:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.textView) TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.button)
public void getData() {
new Thread(new Runnable() {
@Override
public void run() {
//執(zhí)行耗時(shí)操作...
Message msg=new Message();msg.what=7;msg.obj= "網(wǎng)絡(luò)數(shù)據(jù)";
//處理消息時(shí)回調(diào)handler.handleMessage
// handler1.sendMessage(msg);
//處理消息時(shí)回調(diào)callback.handleMessage
// handler2.sendMessage(msg);
//此處使用handler1,2,3 post消息都可以热幔,但是不會執(zhí)行handleMessage
handler3.post(new Runnable() {
@Override
public void run() {
//不會開啟新線程執(zhí)行,handleCallback執(zhí)行run里的代碼讼庇,所以不會報(bào)錯(cuò)
textView.setText("耗時(shí)操作處理結(jié)果");
}
});
}
}).start();
}
Handler handler3=new Handler();
//匿名內(nèi)部類向上轉(zhuǎn)型Handler()方式绎巨,派生子類
Handler handler1=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 7:
textView.setText(msg.obj.toString());//更新UI
break;
}
}
};
//Handler(Callback)方式,不派生子類
Handler handler2=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
textView.setText(msg.obj.toString());//更新UI
return true;//修改為true
}
});
}
7 常見面試問題
(1) 為什么創(chuàng)建 Message 對象推薦使用 Message.obtain()獲取而不是new方式蠕啄?
?Handler 機(jī)制在 Android 系統(tǒng)中使用太頻繁场勤,為了提神效率,為Message設(shè)置了一個(gè)靜態(tài)的消息池歼跟,當(dāng)消息被處理完或移除后和媳,會放入到消息池;下次需要使用Message時(shí)從消息池中取出消息進(jìn)行復(fù)用嘹承;
(2) 簡述MessageQueue 如何入隊(duì)和出隊(duì)窗价?
- 消息入隊(duì):調(diào)用
enquueMessage(msg,when)
;如果消息沒設(shè)置when或者when是0叹卷,直接將消息放到隊(duì)列頭部撼港;否則遍歷隊(duì)列鏈表,找到第一個(gè)大于當(dāng)前消息when的消息結(jié)點(diǎn)骤竹,插入到該節(jié)點(diǎn)前面帝牡;最后會形成一個(gè)按when升序的單鏈表; - 消息出隊(duì):調(diào)用
next()
方法蒙揣,直接取出隊(duì)列頭部消息并返回靶溜;
(3) 發(fā)送消息兩種主要方式 sendMessage(msg)
方式和post(Runnable r)
方式的區(qū)別?
?post(runnable)
方式和sendMessage(msg)
方式發(fā)送消息的聯(lián)系和區(qū)別:
- 聯(lián)系:調(diào)用鏈都是
handler.sendMessageAtTime()
->messageQueue.enqueueMessage()
將Message發(fā)送到消息隊(duì)列懒震; - 區(qū)別:
- 消息內(nèi)容不同:
sendMessage
發(fā)送的消息側(cè)重于傳數(shù)據(jù)罩息,而handleCallback
側(cè)重于傳任務(wù)(Runnable); - 處理消息的方式不同:一般情況个扰,
sendMessage
發(fā)送的消息最終會調(diào)用handler或callback的handleMessage
方法來處理瓷炮;而post(runnable)
發(fā)送的消息最終會調(diào)用handler.handleCallback
方法來處理;
- 消息內(nèi)容不同:
(4) 處理消息有哪幾種方式递宅,他們之間優(yōu)先級娘香?
-
處理消息方式有三種:
handler.handleCallback
方式、callback.handleMessage
方式办龄、handler.handleMessage
方式烘绽;并且優(yōu)先級遞減;
(5) Handler發(fā)送俐填、處理消息有哪幾種方式安接?
- 結(jié)合有以下三種常見的 Handler發(fā)送、處理消息 的方式:
//方式1:send+派生方式
//發(fā)送消息sendMessage英融;構(gòu)造器Handler();處理消息handler.handleMessage;
//注意這種方式由于存在Handler子類內(nèi)部類赫段,可能存在內(nèi)存泄漏的情況呀打,需要處理這種情況
handler1.sendMessage(msg);
//匿名內(nèi)部類向上轉(zhuǎn)型Handler()方式,需要派生子類
Handler handler1=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
textView.setText(msg.obj.toString());//更新UI
break;
}
}
};
//方式2:send+Callback方式
//發(fā)送消息sendMessage糯笙;構(gòu)造器Handler(Callback);處理消息callback.handleMessage;
handler2.sendMessage(msg);
//Handler(Callback)方式,不派生子類
Handler handler2=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
textView.setText(msg.obj.toString());//更新UI
return true;//修改為true
}
});
//方式3:post(Runnable r)方式
//發(fā)送消息post(Runnable r)撩银;構(gòu)造器Handler();處理消息handleCallback;
//此處使用handler1,2,3 post消息都可以给涕,但是不會執(zhí)行handleMessage
handler3.post(new Runnable() {
@Override
public void run() {
//不會開啟新線程,handleCallback執(zhí)行run里的代碼额获,所以不會報(bào)錯(cuò)
textView.setText("耗時(shí)操作處理結(jié)果");
}
});
Handler handler3=new Handler();
(6) 異步消息處理機(jī)制的原理(Handler發(fā)送消息够庙、處理消息的流程)?(高頻題也是本章核心和概要抄邀,很重要)
- Handler消息處理器 作用:發(fā)送消息(任務(wù))耘眨,處理消息;
- Looper消息泵 作用:調(diào)用
Looper.loop()
方法進(jìn)行消息輪詢境肾; - MessageQueue消息隊(duì)列 作用:存放消息的場所剔难,是一個(gè)按when值遞增的單鏈表;
queue.enqueue(msg,when)
用于消息入隊(duì)奥喻;queue.next()
方法用于消息出隊(duì)偶宫,取隊(duì)首消息;
(7) post(Runnable r)
方式是否會開啟新線程环鲤?
??這種方式不會開啟新線程纯趋;
- Runnable對象會包裝成Message對象,r作為Message對象的callback屬性冷离;
- 然后調(diào)用
handler.sendMessageDelay()
->handler.sendMessageAtTime()
->messageQueue.enqueueMessage()
將Message對象發(fā)送到消息隊(duì)列吵冒; - Looper在開啟消息輪詢后,到一定時(shí)間會從消息隊(duì)列中取出該消息對象西剥,交給對應(yīng)的target(Handler對象)進(jìn)行消息分發(fā)痹栖;
- 然后調(diào)用
handler.handleCallback()
方法處理消息,在這個(gè)方法里Runnable任務(wù)會得到執(zhí)行蔫耽;
(8) 區(qū)分兩個(gè)callback结耀?區(qū)分兩個(gè)handleMessage
?
?兩個(gè)callback:
-
Message
的Runnable callback
屬性:使用post(Runnable r)
里的Runnable對象初始化匙铡; -
Handler
的final Callback mCallback
屬性:在Handler(Callback callabck)
構(gòu)造器進(jìn)行初始化图甜;
?兩個(gè)handleMessage
方法,對應(yīng)處理消息的兩種方式:
-
handler.handleMessage
:send+派生方式調(diào)用該方法來處理消息鳖眼; -
callback.handleMessage
:send+Callback方式調(diào)用該方法來處理消息黑毅;
(9) 為什么Handler會造成內(nèi)存泄漏?如何解決钦讳?
(i) 內(nèi)存泄漏的原因矿瘦?
- 一般造成內(nèi)存泄漏的原因:長生命周期對象引用短生命周期對象枕面;
- Handler造成Activity內(nèi)存泄漏的原因:Handler生命周期比Activity長,非靜態(tài)內(nèi)部類默認(rèn)持有外部類的引用,導(dǎo)致Activity對象無法回收(Activity對象先回收時(shí)缚去,Handler對象可能還在處理消息潮秘,此時(shí)Handler對象還持有Activity對象的引用,導(dǎo)致Activity對象無法回收)易结。
(i) 解決辦法枕荞?
?把Handler子類定義為靜態(tài)(static)內(nèi)部類;同時(shí)用WeakReference包裝外部類的對象activity搞动;
- 為什么Handler子類要定義為靜態(tài)(static)內(nèi)部類躏精? ?
?因?yàn)殪o態(tài)內(nèi)部類不持有外部類的引用,所以使用靜態(tài)的Handler不會導(dǎo)致Activity內(nèi)存泄露鹦肿。 - 為什么Handler子類定義為靜態(tài)(static)內(nèi)部類同時(shí)矗烛,還要用WeakReference包裝外部類的對象activity ?
?因?yàn)槲覀冃枰L問外部類的非靜態(tài)成員箩溃,可以通過強(qiáng)引用"activity. "訪問瞭吃,如果直接使用強(qiáng)引用訪問,顯然會導(dǎo)致activity泄露碾篡。
MyHandler mHandler=new MyHandler(this);
private static class MyHandler extends Handler{
//static和WeakReference是為了解決內(nèi)存泄漏
private WeakReference<MainActivity> weakReference;
public MyHandler(MainActivity activity) {
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
MainActivity activity=weakReference.get();
if(activity != null){//判空是為了避免空引用異常
activity.textView.setText(msg.obj.toString());
}
break;
}
}
}
(10) 為何Looper.loop()
死循環(huán)不會造成應(yīng)用卡死虱而?
?Looper.loop()
不會造成應(yīng)用卡死,因?yàn)槔锩媸褂昧?strong>Linux 的epoll機(jī)制开泽;
(11) 創(chuàng)建Handler前要注意什么牡拇?
?創(chuàng)建Handler對象前必須調(diào)用Looper.prepare()
方法創(chuàng)建一個(gè)Looper對象馋嗜;值得一體的是够吩,子線程中必須手動調(diào)用Looper.prepare()
方法玻淑,而主線程中可以不調(diào)用卧秘;因?yàn)橹骶€程ActivityThread
的main方法中默認(rèn)調(diào)用了Looper.prepareMainLooper()
方法暮蹂,這個(gè)方法會調(diào)用Looper.prepare()
方法創(chuàng)建一個(gè)主線程Looper對象夭委;
(12) 異步消息處理機(jī)制是如何保證消息處理器的唯一性(即某條消息的發(fā)送者和處理者是同一Handler對象)杆麸?
?在Handler的enqueueMessage
方法中會把自引用賦值給被發(fā)送的Message的target屬性旷赖;而在Looper的loop
方法中會調(diào)用msg.target.dispatchMessage(msg)
來分發(fā)辅髓、處理消息泣崩;
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
//...
}
//Looper.java
public static void loop() {
//...
msg.target.dispatchMessage(msg);
//...
}
(13) 子線程中是否可以創(chuàng)建Handler對象?
?子線程中可以創(chuàng)建Handler對象洛口,但是子線程在創(chuàng)建Handler對象前必須手動調(diào)用Looper.prepare()
方法創(chuàng)建Looper對象矫付;
new Thread(new Runnable() {
@Override
public void run() {
if(Looper.myLooper()==null){//保證一個(gè)線程只有一個(gè)Looper
Looper.prepare();//創(chuàng)建Looper
}
Handler handler1=new Handler();
Looper.loop();//開啟消息輪詢
//主線程Looepr對象早已創(chuàng)建,并早已開啟消息輪詢
Handler handler2=new Handler(Looper.prepareMainLooper());
}
}).start();
ps:如果在主線程中使用子線程中創(chuàng)建的Handler對象的引用發(fā)送消息第焰,最后消息是在子線程中處理的买优;這樣就實(shí)現(xiàn)了主線程向子線程發(fā)送消息,而在本文6.2節(jié)中的sample代碼中實(shí)現(xiàn)了子線程向主線程發(fā)送消息;所以兩個(gè)線程可以通過Handler進(jìn)行雙向通信杀赢;
(14) Handler 與 Looper 是如何關(guān)聯(lián)的?
- 對于有Looper參數(shù)的構(gòu)造器Handler(looper):直接通過構(gòu)造器參數(shù)設(shè)置關(guān)聯(lián)的Looepr對象烘跺;
- 對于無Looper參數(shù)的Handler構(gòu)造器:無論是
Handler()
還是Handler(callback)
都會調(diào)用下面的構(gòu)造器,在這個(gè)構(gòu)造其中會為當(dāng)前Handler關(guān)聯(lián)當(dāng)前線程的Looper對象脂崔;
public Handler(Callback callback, boolean async) {
//...
mLooper = Looper.myLooper();//獲取當(dāng)前線程的Looepr對象
//...
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//獲取當(dāng)前線程的本地變量
}
(15) Thread 與 Looper 是如何關(guān)聯(lián)的?
?Looper 與 Thread 之間是通過 ThreadLocal
關(guān)聯(lián)的滤淳,這個(gè)可以看 Looper.prepare(quitAllowed)
方法:
// Looper.java:93
private static void prepare(boolean quitAllowed) {
//...
sThreadLocal.set(new Looper(quitAllowed));//設(shè)置當(dāng)前線程本地變量
}
?Looper 類有一個(gè) ThreadLocal 類型的 sThreadLocal靜態(tài)屬性,Looper通過它的 get 和 set 方法來賦值和取值砌左;
?由于 ThreadLocal是與當(dāng)前線程是綁定的娇钱,所以我們只要把 Looper 與 ThreadLocal 綁定了,那 Looper 和 Thread 也就關(guān)聯(lián)上了绊困;
(16) 如何在子線程中獲取當(dāng)前線程的 Looper?
Looper.myLooper();//獲取當(dāng)前線程的Looepr對象
?內(nèi)部原理就是sThreadLocal.get()
:
// Looper.java:203
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
(16) 如何在任意線程獲取主線程的 Looper?
Looper.getMainLooper();//獲取主線程的Looper對象
?這個(gè)在我們開發(fā) API時(shí)特別有用适刀,畢竟你不知道開發(fā)者在使用你的API時(shí)會在哪個(gè)線程初始化Looper秤朗,所以我們在創(chuàng)建 Handler 時(shí)每次都通過指定主線程的 Looper 的方式保證API正常運(yùn)行。所以一般使用主線程Looper來進(jìn)行異步消息處理笔喉;
(17) 如何判斷當(dāng)前線程是不是主線程取视?
?
//方式1
Looper.myLooper() == Looper.getMainLooper();
//方式2
Looper.getMainLooper().getThread() == Thread.currentThread();
//方式3:方式2簡化版
Looper.getMainLooper().isCurrentThread();
(18) Looper.loop()
方法會退出嗎?
?不會自動退出常挚,但是我們可以手動調(diào)用 looper.quit()
或looper.quitSafely()
方法退出消息輪詢作谭。
?這兩個(gè)方法都會調(diào)用MessageQueue#quit(boolean)
方法讓MessageQueue#mQuitting
屬性置為true
,標(biāo)記消息隊(duì)列已退出奄毡;消息隊(duì)列退出后折欠,MessageQueue#next()
方法發(fā)現(xiàn)已經(jīng)調(diào)用過 MessageQueue#quit(boolean) 時(shí)會 return null ;然后Looper.loop()
方法退出消息輪詢吼过;
?如果looper.quit()
锐秦,looper.quitSafely()
,MessageQueue#quit(boolean)
都不手動調(diào)用并且消息隊(duì)列為空,消息隊(duì)列不會退出盗忱;next()
方法會一直死循環(huán)(有的說法稱為next方法阻塞)酱床,loop()
方法會在Message msg = queue.next();
處阻塞等待新消息到達(dá)消息隊(duì)列,繼續(xù)消息輪詢趟佃。所以建議當(dāng)所有Message都被處理完之后手動調(diào)用looper.quit()
或looper.quitSafely()
方法退出消息輪詢扇谣,避免loop()
方法一直阻塞等待。
//Looper.java#322
public void quit() {//Looper退出
mQueue.quit(false);
}
//Looper.java#338
public void quitSafely() {
mQueue.quit(true);
}
//Looper.java#137
public static void loop() {//開啟消息輪詢
//...
for (;;) {
Message msg = queue.next(); // 當(dāng)消息隊(duì)列為空且未退出時(shí)闲昭,next方法會阻塞
if (msg == null) {
//next返回null表明消息隊(duì)列已退出
return;//結(jié)束輪詢罐寨,loop方法唯一出口
}
//...
msg.target.dispatchMessage(msg);
//...
}
}
//MessageQueue.java#416
void quit(boolean safe) {
//...
mQuitting = true;
//...
}
//MessageQueue.java#310
Message next() {
//...
for (;;) {
synchronized (this) {
//...
Message msg = mMessages;
//...
if (msg != null) {
if (now < msg.when) {
//...
}else{
//...
return msg;//取到非空消息退出
}
}else{
// No more messages.
nextPollTimeoutMillis = -1;
}
//若消息隊(duì)列已退出,返回true退出死循環(huán)
if (mQuitting) {
dispose();
return null;
}
}
(19) MessageQueue#next()
方法在消息隊(duì)列為空時(shí)會阻塞汤纸,如何恢復(fù)衩茸?
?使用Handler的sendMessage、post 等一系列方法發(fā)送消息,這些發(fā)送消息的方法會調(diào)用MessageQueue#enqueueMessage
將新消息入隊(duì)楞慈,從而使得next()
方法不再阻塞幔烛;
(20) IdleHandler作用和使用場景?
?把頁面啟動時(shí)的復(fù)雜邏輯交給IdleHandler去處理囊蓝,這樣可以讓主線程Handler先處理完相關(guān)UI邏輯后再去處理復(fù)雜邏輯饿悬,可以減少頁面啟動白屏?xí)r間,從而優(yōu)化頁面啟動聚霜;
(21) 子線程為何不能訪問UI?
- 源碼角度:當(dāng)訪問UI時(shí)狡恬,ViewRootImpl會調(diào)用checkThread()方法檢查當(dāng)前線程是哪個(gè)線程,如果不是UI線程會拋出異常;
- 線程安全角度:訪問UI不是線程安全的蝎宇;
- 訪問UI為什么不加鎖:邏輯復(fù)雜弟劲、效率低;