Handler
一套Android的消息傳遞機制/異步通信機制江掩。在多線程的應(yīng)用場景中逗抑,將工作線程中需要更新UI的操作信息傳遞到UI主線程,從而實現(xiàn)工作線程對UI的更新處理蝴蜓,最終實現(xiàn)異步消息的處理仅颇。
- 消息
Message单默,是線程間通訊的數(shù)據(jù)單元(即Hnadler接受&處理的消息對象)。存儲需要操作的通信信息忘瓦。
- 消息隊列
MessageQueue搁廓,一種數(shù)據(jù)結(jié)構(gòu)(存儲特點:先進(jìn)先出)。存儲Handler發(fā)送過來的消息(Message)耕皮。
- 處理者
Handler境蜕,是主線程與子線程的通信媒介,也是線程消息的主要處理者明场。添加消息(Message)到消息隊列汽摹、處理循環(huán)器(Looper)分派過來的消息(Message)。
- 循環(huán)器
Looper苦锨,是消息隊列(MessageQueue)與處理者(Handler)的通信媒介。消息循環(huán)即(循環(huán)取出消息隊列的消息)趴泌、消息分發(fā)(將取出的消息發(fā)送給對應(yīng)的處理者)
- 系統(tǒng)為什么不允許在子線程中去訪問UI
這是因為Android的UI線程是不安全的舟舒,多線程并發(fā)訪問可能導(dǎo)致UI控件處于不可預(yù)期的狀態(tài)。
- 為什么不加鎖呢
缺點有兩個:首先加上鎖機制會讓UI控件的訪問邏輯變得復(fù)雜嗜憔,其次鎖機制會降低UI的訪問效率秃励,鎖會堵塞默寫線程的執(zhí)行。
Handler創(chuàng)建時會采用當(dāng)前線程的Looper來構(gòu)建內(nèi)部消息循環(huán)系統(tǒng)吉捶,如果當(dāng)前線程沒有Looper就會報錯夺鲜。在當(dāng)前線程創(chuàng)建一個Looper皆尔,或者在有Looper的線程中創(chuàng)建Handler。
Handler通過post方法將一個Runnable投遞到Handler內(nèi)部中的Looper中去處理币励,也可以通過Handler的send方法發(fā)送一個消息慷蠕,這個消息同樣會在Looper中處理。(其實post方法最終也是由send方法完成的)食呻。
當(dāng)Handler的send方法被調(diào)用時流炕,它會調(diào)用MessageQueue的enqueueMessage方法將消息放入消息隊列中,然后Looper發(fā)現(xiàn)有新消息到來時就會處理這個消息仅胞,最終消息中的Runnable或者Handler的handMessage方法就會被調(diào)用每辟。
注意Looper是運行在創(chuàng)建Handler的線程中的,這樣一來Handler中的業(yè)務(wù)邏輯就被切換到創(chuàng)建Handler的線程中去了干旧。
ThreadLocal的工作原理
ThreadLocal是一個線程內(nèi)部的數(shù)據(jù)存儲類渠欺,通過它可以在指定線程中存儲數(shù)據(jù),數(shù)據(jù)存儲之后椎眯,值可以在指定的線程中獲取到存儲數(shù)據(jù)峻堰,其他線程中獲取不到。
Looper盅视、ActivityThread以及AMS中都用到了ThreadLocal捐名。
Looper另一個使用場景是復(fù)雜邏輯下的對象傳遞,比如監(jiān)聽器的傳遞闹击。它可以讓監(jiān)聽器在線程內(nèi)作為一個全局對象的存镶蹋。
- 不同線程訪問同一個ThreadLocal的get方法,ThreadLocal內(nèi)部會從各自線程中取出一個數(shù)組赏半,然后再從數(shù)組中根據(jù)當(dāng)前ThreadLocal的索引去查找對應(yīng)的value值贺归。
- ThreadLocal的set方法
public void set(T value){
Thread currentThread = Thread.currentThread();
Values value = values(currentThread);
if(values == null){
values = initalizeValues(currentThread);
}
values.put(this,value);
}
首先通過values方法去獲取當(dāng)前線程中ThreadLocal中的數(shù)據(jù)。在Thread類的內(nèi)部有一個成員專門用于存儲ThreadLocal中的數(shù)據(jù):ThreadValues.localValues断箫。在localValues內(nèi)部有一個table數(shù)組:private Object[] table拂酣,ThreadLocal的值就是存在在這個table數(shù)組中的。
ThreadLocal的值在table數(shù)組的存儲位置總是ThreadLocal的reference字段所標(biāo)識的對象的下一個位置仲义,比如ThreadLocal的reference對象在table數(shù)據(jù)中的索引是index婶熬,那么ThreadLocal的值在table數(shù)組中的索引就是index+1,table[index+1]=value埃撵。
- ThreadLocal的set方法
public T get(){
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if(values !=null){
Object[] table = values.table;
int index = hash&values.mask;
if(this.reference == table[index]){
return (T)table[index+1];
}
}else{
values = initalizeValues(currentThread);
}
return (T)values.getAfterMiss(this);
}
從ThreadLocal的set/get方法中可以看出赵颅,他們操作的對象都是當(dāng)前線程中l(wèi)ocalValues的table數(shù)組,因此不同線程訪問同一個ThreadLocal的set和get方法暂刘,他們對ThreadLocal所做的讀寫/寫入操作僅限于各自線程內(nèi)部饺谬。這就是為什么ThreadLocal可以在多個線程中互補干擾的存儲和獲取數(shù)據(jù)。
MessageQueue的工作原理
MessageQueuq雖然叫做消息隊列谣拣,但實際是通過一個單鏈表結(jié)構(gòu)來維護(hù)的消息隊列募寨,單鏈表在插入和刪除上有優(yōu)勢
- enqueueMessage(插入操作)
就是單鏈表的插入操作
- next(讀取消息族展,并從消息隊列中刪除)
next方法是一個無線循環(huán)的方法,如果消息隊列中沒有消息拔鹰,next方法就會一直堵塞在這里仪缸,當(dāng)有新消息是,next方法會返回這條消息并將從單鏈表中移除格郁。
Looper的工作原理
Looper在Android消息機制中扮演著消息循環(huán)的角色腹殿,具體來說就是它會不停地從MessageQueue中查看是否有新消息,如果有新消息就會立即處理例书,否則會一直堵塞在那里锣尉。
- 構(gòu)造方法
private Looper(boolean quitAllowed){
//創(chuàng)建消息隊列
mQueue = new MessageQueue(quitAllowed);
//保存當(dāng)前線程對象
mThread = Thread.currentThread();
}
- Looper的創(chuàng)建
new Thread("Thread#2"){
@Override
public void run(){
Looper.prepare();
Handler handler = new Handler();
Looper.loop();//開啟消息循環(huán)
};
}.start();
Looper除了prepare方法外,還提供prepareMainLooper方法决采,這個方法主要是給主線程也就數(shù)ActivityThread創(chuàng)建Looper使用自沧,其本質(zhì)也是通過prepare實現(xiàn)的。
由于主線程Looper比較特殊树瞭,所以Looper提供一個getMainLooper方法拇厢,通過它可以在任何地方獲取主線程的Looper
- Looper的退出
quit直接退出Looper
quitSafely設(shè)定一個退出標(biāo)記,然后把消息隊列中已有消息處理完后安全退出晒喷。
Looper退出后通過Handler發(fā)送消息就是失敗孝偎,這個時候Handler的send方法返回就是false。
在子線程中凉敲,如果手動創(chuàng)建了Looper衣盾,那么在所有的事情處理完成后應(yīng)立即調(diào)用quit方法來終止消息循環(huán),否則在這個子線程就會一直處于等待的狀態(tài)爷抓。
- 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){
//沒有消息势决,退出死循環(huán)
return;
}
Printer logging=me.mLogging;
if(logging!=null){
logging.println(">>>> Dispatching to"+msg.target+" "+msg.callback+": "+msg.what);
}
msg.target.dispatchMessage(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.getCalss().getName()+" "
+msg.callback+" what="+msg.what);
}
msg.recycleUnchecked();
}
}
Hnadler的工作原理
Hnadler的工作主要包含消息的發(fā)送和接收過程蓝撇。通過post或send來實現(xiàn)果复。post最終也是send實現(xiàn)的。
- send方法
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);
}
可以發(fā)現(xiàn)Handler發(fā)送消息過程僅僅是向消息隊列中插入一條消息渤昌,MessageQueue的next方法就會返回一條消息給Looper虽抄,Looper收到消息后開始處理,最終消息由Looper交由Handler處理耘沼,即Handler的dispatchMessage方法被調(diào)用极颓,這是Handler進(jìn)入消息處理階段。
- dispatchMessage方法
public void dispatchMessage(Message msg){
if(msg.callback!=null){
handlerCallback(msg);
}else{
if(mCallback!=null){
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg);
}
}
主線程的消息循環(huán)
Android的主線程就是ActivityThread群嗤,主線程的入口方法是main,在main方法中系統(tǒng)會通過Looper.prepareMainLooper()方法來創(chuàng)建主線程的Looper和MessageQueue兵琳,并通過Looper.loop()來開啟主線程的消息循環(huán)狂秘。
- main方法
public static void main(String[] args){
...
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread=new ActivityThread();
thread.attach(false);
if(sMainThreadHandler==null){
sMainThreadHandler==thread.getHandler();
}
AsyncTask.init();
if(false){
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG,"ActivityThread"));
}
Looper.loop();
throws new RuntimeException("Main thread loop unexpectedly exited");
}
主線程消息循環(huán)開啟后骇径,ActivityThread還需要一個Handler來和消息隊列進(jìn)行交互,這個Handler就是ActivityThread.H者春,它內(nèi)部定義了一組消息模型破衔,主要包含四大組件的啟動和停止過程。
ActivityThread通過applicationThread和AMS進(jìn)行進(jìn)程間通信钱烟,AMS以進(jìn)程間通信的方式完成ActivityThread的請求后會回調(diào)ApplicationThread中Binder方法晰筛,然后ApplicationThread會向H發(fā)送消息,H收到消息后會將ApplicationThread中的邏輯切換到ActivityThread中執(zhí)行拴袭,即切換到主線程中去執(zhí)行读第,這個過程就是主線程中的消息模型。