handler
畫圖說明:
我在學(xué)習(xí)和使用handler的時(shí)候,對(duì)與它相關(guān)的源代碼進(jìn)行的研究,說到handler機(jī)制,就要設(shè)計(jì)到5個(gè)類(畫圖),
Handler、MessageQueue、Looper、Thread、還有一個(gè)Message;
Message是消息,它由MessageQueue統(tǒng)一列隊(duì),由Handler處理。
Handler是處理者,他負(fù)責(zé)發(fā)送和處理Message消息。
MessageQueue指消息隊(duì)列,它用來存放Handler發(fā)送過來的隊(duì)列,并且按照先入先出的規(guī)則執(zhí)行。
Looper的作用就像抽水的水泵,它不斷的從MessageQueue中去抽取Message并執(zhí)行。
Thread線程,是消息循環(huán)的執(zhí)行場(chǎng)所。
知道了這幾個(gè)類就可以說說消息機(jī)制的原理了,在創(chuàng)建Activity之前,當(dāng)系統(tǒng)啟動(dòng)的時(shí)候,先加載ActivityThread這個(gè)類,在這個(gè)類的main函數(shù)中,調(diào)用Looper.prepareMainLooper()進(jìn)行初始化Looper對(duì)象,然后創(chuàng)建主線程的handler對(duì)象,隨后才創(chuàng)建ActivityThread對(duì)象,最后調(diào)用Looper.loop()方法,不斷的進(jìn)行輪詢消息隊(duì)列中的消息。也就是說,在ActivityThread和Activity創(chuàng)建之前,就已經(jīng)開啟了Looper的loop()方法,不斷的進(jìn)行輪詢消息。
我們可以畫圖來說明handler機(jī)制的原理:
我們通過Message.obtain()準(zhǔn)備消息數(shù)據(jù)之后,
第一步是使用sendMessage():通過Handler將消息發(fā)送給消息隊(duì)列
第二步、在發(fā)送消息的時(shí)候,使用message.target=this為handler發(fā)送的message貼上當(dāng)前handler的標(biāo)簽
第三步、開啟HandlerThread線程,執(zhí)行run方法。
4、在HandlerThread類的run方法中開啟輪詢器進(jìn)行輪詢:調(diào)用Looper.loop()方法進(jìn)行輪詢消息隊(duì)列的消息
5、在消息隊(duì)列MessageQueue中enqueueMessage(Message msg, long when)方法里,對(duì)消息進(jìn)行入列,即依據(jù)傳入的時(shí)間進(jìn)行消息入列(排隊(duì))
6、輪詢消息:與此同時(shí),Looper在不斷的輪詢消息隊(duì)列
7、在Looper.loop()方法中,獲取到MessageQueue對(duì)象后,從中取出消息(Message msg = queue.next()),如果沒有消息會(huì)堵塞
8、分發(fā)消息:從消息隊(duì)列中取出消息后,調(diào)用msg.target.dispatchMessage(msg);進(jìn)行分發(fā)消息
9、將處理好的消息分發(fā)給指定的handler處理,即調(diào)用了handler的dispatchMessage(msg)方法進(jìn)行分發(fā)消息。
10、在創(chuàng)建handler時(shí),復(fù)寫的handleMessage方法中進(jìn)行消息的處理
11、回收消息:在消息使用完畢后,在Looper.loop()方法中調(diào)用msg.recycle(),將消息進(jìn)行回收,即將消息的所有字段恢復(fù)為初始狀態(tài)。
12. what帶字段,obj帶數(shù)據(jù), 創(chuàng)建方法 new Message 或Message.obtain()
handler機(jī)制?即handler的作用
在Android的UI開發(fā)中,我們經(jīng)常會(huì)使用Handler來控制主UI程序的界面變化。有關(guān)Handler的作用,
我們總結(jié)為:與其他線程協(xié)同工作,接收其他線程的消息并通過接收到的消息更新主UI線程的內(nèi)容。
我們假設(shè)在一個(gè)UI界面上面,有一個(gè)按鈕,當(dāng)點(diǎn)擊這個(gè)按鈕的時(shí)候,會(huì)進(jìn)行網(wǎng)絡(luò)連接,并把網(wǎng)絡(luò)上的一個(gè)字符串拿下來顯示到界面上的一個(gè) TextView上面,這時(shí)就出現(xiàn)了一個(gè)問題,如果這個(gè)網(wǎng)絡(luò)連接的延遲過大,可能是10秒鐘甚至更長(zhǎng),那我們的界面將處于一直假死狀態(tài),而如果這段時(shí)間超 過5秒鐘的話,程序會(huì)出現(xiàn)異常。
這時(shí)我們會(huì)想到使用線程來完成以上工作,即當(dāng)按鈕被按下的時(shí)候新開啟一個(gè)線程來完成網(wǎng)絡(luò)連接工作,并把得到的結(jié)果更新到UI上面。但是,這時(shí)候又會(huì) 出現(xiàn)另一個(gè)問題,在Android中,主線程是非線程安全的,也就是說UI的更新只能在本線程中完成,其他線程無法直接對(duì)主線程進(jìn)行操作。
為了解決以上問題,Android設(shè)計(jì)了Handler機(jī)制,由Handler來負(fù)責(zé)與子線程進(jìn)行通訊,從而讓子線程與主線程之間建立起協(xié)作的橋梁,使Android的UI更新的問題得到完美的解決。接下來ATAAW.COM舉例來詮釋Handler的基本使用方法。
A、Handler的工作原理
一般情況下,在主線程中我們綁定了Handler,并在事件觸發(fā)上面創(chuàng)建新的線程用于完成某些耗時(shí)的操作,當(dāng)子線程中的工作完成之后,會(huì)對(duì)Handler發(fā)送一個(gè)完成的信號(hào),而Handler接收到信號(hào)后,就進(jìn)行主UI界面的更新操作。
B、Handler與子線程協(xié)作實(shí)例
1、創(chuàng)建Handler實(shí)現(xiàn)類,在主UI所在類中的內(nèi)部類
1 class MyHandler extends Handler {
2 public MyHandler() {
1 }
1 public MyHandler(Looper L) {
1 super(L);
1 }
1 // 重寫handleMessage方法,接受數(shù)據(jù)并更新UI
1 @Override
1 public void handleMessage(Message msg) {
2 super.handleMessage(msg);
1 //此處根據(jù)msg內(nèi)容進(jìn)行UI操作
1 }
1 }
2、子線程的實(shí)現(xiàn)
1 class MyThread implements Runnable {
1 public void run() {
1 Message msg = new Message();
1 Bundle b = new Bundle();
1 b.putString("cmd", "update");
1 msg.setData(b);
1 MainActivity.this.myHandler.sendMessage(msg);//通知Handler更新UI
2 }
1 }
通過以上的兩個(gè)實(shí)現(xiàn),我們只需要在MainActivity中聲明MyHandler實(shí)例對(duì)象就可以完成線程之間的通訊和界面的更新操作。
MyHandler myHandler = newMyHandler();
調(diào)用流程
Message類的obtain方法
消息隊(duì)列順序的維護(hù)是使用單鏈表的形式來維護(hù)的
把消息池里的第一條數(shù)據(jù)取出來,然后把第二條變成第一條
if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; }
創(chuàng)建Handler對(duì)象時(shí),在構(gòu)造方法中會(huì)獲取Looper和MessageQueue的對(duì)象
public Handler() {
...
//拿到looper
mLooper = Looper.myLooper();
...
//拿到消息隊(duì)列
mQueue = mLooper.mQueue;
mCallback = null;
}
查看myLooper方法體,發(fā)現(xiàn)Looper對(duì)象是通過ThreadLocal得到的,在查找ThreadLocal的set方法時(shí)發(fā)現(xiàn)
Looper是直接new出來的,并且在Looper的構(gòu)造方法中,new出了消息隊(duì)列對(duì)象
sThreadLocal.set(new Looper());
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
sThreadLocal.set(new Looper())是在Looper.prepare方法中調(diào)用的
prepare方法是在prepareMainLooper()方法中調(diào)用的
public static final void prepareMainLooper() {
prepare();
...
}
在應(yīng)用啟動(dòng)時(shí),主線程要被啟動(dòng),ActivityThread會(huì)被創(chuàng)建,在此類的main方法中
public static final void main(String[] args) {
...
//創(chuàng)建Looper和MessageQueue
Looper.prepareMainLooper();
...
//輪詢器開始輪詢
Looper.loop();
...
}
Looper.loop()方法中有一個(gè)死循環(huán)
while (true) {
//取出消息隊(duì)列的消息,可能會(huì)阻塞
Message msg = queue.next(); // might block
...
//解析消息,分發(fā)消息
msg.target.dispatchMessage(msg);
...
}
Linux的一個(gè)進(jìn)程間通信機(jī)制:管道(pipe)。原理:在內(nèi)存中有一個(gè)特殊的文件,這個(gè)文件有兩個(gè)句柄(引用),一個(gè)是讀取句柄,一個(gè)是寫入句柄
主線程Looper從消息隊(duì)列讀取消息,當(dāng)讀完所有消息時(shí),進(jìn)入睡眠,主線程阻塞。子線程往消息隊(duì)列發(fā)送消息,并且往管道文件寫數(shù)據(jù),主線程即被喚醒,從管道文件讀取數(shù)據(jù),主線程被喚醒只是為了讀取消息,當(dāng)消息讀取完畢,再次睡眠
Handler發(fā)送消息,sendMessage的所有重載,實(shí)際最終都調(diào)用了sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
...
//把消息放到消息隊(duì)列中
sent = queue.enqueueMessage(msg, uptimeMillis);
...
}
enqueueMessage把消息通過重新排序放入消息隊(duì)列
final boolean enqueueMessage(Message msg, long when) {
...
final boolean needWake;
synchronized (this) {
...
//對(duì)消息的重新排序,通過判斷消息隊(duì)列里是否有消息以及消息的時(shí)間對(duì)比
msg.when = when;
Message p = mMessages;
//把放入消息隊(duì)列的消息置為消息隊(duì)列第一條消息
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
//判斷時(shí)間順序,為剛放進(jìn)來的消息尋找合適的位置
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
//喚醒主線程
if (needWake) {
nativeWake(mPtr);
}
return true;
}
Looper.loop方法中,獲取消息,然后分發(fā)消息
//獲取消息隊(duì)列的消息
Message msg = queue.next(); // might block
...
//分發(fā)消息,消息由哪個(gè)handler對(duì)象創(chuàng)建,則由它分發(fā),并由它的handlerMessage處理
msg.target.dispatchMessage(msg);
message對(duì)象的target屬性,用于記錄該消息由哪個(gè)Handler創(chuàng)建,在obtain方法中賦值
總結(jié)
- 上一篇: Equals与==
- 下一篇: 7nm工艺什么意思(中芯国际掌握7nm工