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