一、UI線程
什麼是UI線程?
Android應用在啓動時間會首先創建一個主線程(main thread),它是應用程序的入口,主要負責管理UI以及分發事件,習慣上稱之爲UI線程(UI thread)。
1、 不是線程安全的,對UI的操作操作必須在UI線程中進行,否則系統將拋出異常;
ProgressBar通過setProgress更新UI?
在更新進度的時候會判斷當前線程是否爲UI線程,是UI線程直接調用刷新方法,不是的話就調用view的post方法,將runable放到UI線程的消息隊列等待處理,這個就涉及到Handler,下文再講。
Android提供了以下一些方法,從其他線程訪問UI線程:
· Activity.runOnUiThread(Runnable)
· View.postDelayed(Runnable,long)
2、UI線程不能被阻塞,阻塞時間超過5s,出現ANR,影響用戶體驗,對於耗時的操作一定要在非UI線程中執行。
二、消息系統與消息模型
Android實現自己的消息系統,抽象出Message、MessageQueue、Looper、Handler等概念,這些組件巧妙結合形成了Android的消息模型,首先我們瞭解一下消息系統的構成要素和基本原理。
2.1 消息系統構成要素和基本原理
從一般的系統設計層次來說,基本的消息循環系統需要包含以下幾個要素。
●消息隊列
●發送消息
●消息讀取
●消息分發
●消息循環線程
消息系統必須要依賴一個消息循環來輪詢自己的消息隊列,如果有消息進來,就調用消息處理函數,根據消息類型及其參數做相應的處理。消息系統要運作起來,必定有消息的產生和消費。暫且把產生消息的線程稱作生產者線程,把消費者線程稱作消費者線程。生產者線程將消息發送到消息隊列,消費者線程從消息隊列中取出消息進行相應的處理。當消息隊列中沒有消息時,消費者線程便進入了掛起狀態,而當有新的消息到達時,消費者線程會被喚醒繼續運行。當然生產者也可以是消費者。
圖2.1 基本的消息循環模型
2.2 消息模型基本原理
Android的消息系統使用了Message、MessageQueue、Looper、Handler等概念,從消息系統的基本原理可以知道這些都是概念包裝,本質的東西是消息隊列中消息的分發處理方法。Android巧妙地利用了對象抽象技術抽象出了Looper和Handler的概念,並在Looper和Handler概念的基礎上,通過View的處理框架,十分完美地實現了消息分發的功能。下面對這幾個概念進行詳細介紹。
2.2.1 Message
消息對象,它是信息的載體,線程間通訊的數據單元。例如後臺線程在處理數據完畢後需要更新UI,則可發送一條包含更新信息的Message給UI線程。
Message通常存放在消息隊列(MessageQueue)中,一個MessageQueue可以包含多個Message。
創建實例,obtain(),該方法有多個重載,不一一介紹。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
該方法獲取Message時並不是直接創建一個新的實例,而是先從Message Pool(消息池)中查看有沒有可用的Message實例,如果有,則直接複用這個Message實例;如果沒有,創建一個新的Messages實例。
消息回收,recycle()
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
系統處理完消息之後,並不是直接將消息消除,而是放到消息池當中(最大值爲50個,若消息池中已經有50個Message,則丟棄不保存)。
這麼做的好處就是消息不被使用時,並不作爲垃圾回收,而是放入消息池,可供下次創建消息時使用。消息池提高了消息對象的複用,減少系統垃圾回收的次數。
Message相關內容就說這麼多。
2.2.2 MessageQueue
是一種數據結構,具有先進先出的特點,用來存放消息隊列。每個線程最多擁有一個MessagQueue。創建線程時,並不會自動創建對應的MessageQueue。通常使用Looper對象對線程的MessageQueue進行管理。Android應用程序在創建主線程時,會默認創建一個Looper對象,該對象創建時,會自動創建一個MessageQueue。其他線程不會自動創建Looper,需要的時候可以通過調用Looper的prepare()函數創建。
MessageQueue封裝在Looper中,用戶一般很少去接觸,不再詳細介紹。
2.2.3 Looper
MessageQueue的管理者,每一個MessageQueue都不能脫離Looper而單獨存在。
Looper對象的創建是通過prepare函數來實現的。同時每一個Looper對象和一個線程關聯。通過調用Looper.myLooper()可以獲得當前線程的Looper對象 。創建一個Looper對
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
Looper對象創建好後,Looper線程要真正工作,需要調用loop()方法,它不斷從自己的MQ中取出隊頭的消息,將Message交給Message的target進行處理,處理完之後,調用Message.recycle()放入消息池中,消息隊列中沒有消息時會退出循環。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of thisthread is that of the local process,
// and keep track of what that identitytoken actually is.
Binder.clearCallingIdentity();
final long ident =Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); //might block
if (msg == null) {
// No message indicates thatthe message queue is quitting.
return;
}
.................................
msg.target.dispatchMessage(msg);
.................................
msg.recycle();
}
}
除了主線程有默認的Looper,其他線程默認是沒有Looper對象的,所以,不能接受Message。如需要接受,自己定義一個Looper對象(通過prepare函數),這樣該線程就有了自己的Looper對象和MessageQueue數據結構了。 具有Looper對象的線程我們也稱之爲“Looper線程”。
如何創建一個Looper線程?
public class LooperThread extends Thread {
@Override
publicvoid run() {
// 將當前線程初始化爲Looper線程
Looper.prepare();
// ...其他處理,如實例化handler
// 開始循環處理消息隊列
Looper.loop();
}
}
但是Android是一個比較成熟的系統,所以我們一般不直接去創建Looper線程,android提供了HandlerThread,這是一個帶有消息循環的線程,它有自己的消息隊列,能夠接收其他線程發送的消息。
除了prepare()和loop()方法,Looper類還提供了一些有用的方法,
public static final Looper myLooper() {
// 在任意線程調用Looper.myLooper()返回的都是那個線程的looper
return(Looper)sThreadLocal.get();
}
public Thread getThread() {//得到looper對象所屬線程
return mThread;
}
public void quit() {//結束looper循環
// 創建一個空的message,它的target爲NULL,表示結束循環消息
Message msg = Message.obtain();
// 發出消息
mQueue.enqueueMessage(msg, 0);
}
通過以上的一些介紹,對Looper可以總結爲以下幾點:
●每個線程有且最多只能有一個Looper對象;
●Looper內部有一個消息隊列,loop()方法調用後線程開始不斷從隊列中取出消息執行;
●Looper使一個線程變成Looper線程。
那麼,如何將將消息添加到消息隊列以及處理消息呢?且看下文。
2.2.4 Handler
消息的發送者和處理者,扮演了往MQ上添加消息和處理消息的角色(只處理由自己發出的消息)。
Handler對象通過obtainMessage()方法,將需要傳遞的信息封裝成Message對象,調用sendMessage()方法將消息傳遞給Looper,然後由Looper將Message放入MessageQueue中,具體操作在Looper中已經介紹,不再贅述。最後通過Message對應的Handler的handleMessage()進行處理。
Handler只能在它所在的線程上下文中取得消息隊列,然後對消息隊列操作,如果外部線程需要向某個線程發送消息,必須先獲取某個線程中的任意Handler對象,然後通過Handler對象進行發送消息或者刪除消息。
Handler創建時會關聯一個Looper,默認的構造方法將關聯當前線程的looper,不過這也是可以設定的。
public Handler(Callback callback, booleanasync) {
....................
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handlerinside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callbackcallback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler發送消息
Handler使用 post(Runnable), postAtTime(Runnable,long),
postDelayed(Runnable,long), sendEmptyMessage(int),sendMessage(Message), sendMessageAtTime(Message,long)和 sendMessageDelayed(Message,long)這些方法向MQ上發送消息,光看這些API你可能會覺得handler能發兩種消息,一種是Runnable對象,一種是Message對象,這是直觀的理解,但其實post發出的Runnable對象最後都被封裝成message對象了。以post(Runnable r)爲例
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static MessagegetPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
Handler發送的消息有如下特點:
●message.target爲該handler對象,這確保了looper執行到該message時能找到處理它的handler,即loop()方法中的關鍵代碼;
●post發出的Message,其callback爲Runnable對象。
Handler處理消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if(mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage()方法裏,如何處理Message則由用戶指定,三個判斷,優先級從高到低:
●Message裏面的Callback,一個實現了Runnable接口的對象,其中run函數做處理工作;
●Handler裏面mCallback指向的一個實現了Callback接口的對象,由其handleMessage進行處理;
●處理消息Handler對象對應的類繼承並實現了其中handleMessage函數,通過這個實現的handleMessage函數處理消息。
通過對以上幾個概念的介紹,我們對消息的處理流程有了一定理解,其流程基本可以概括爲一下幾點:
●包裝Message對象(指定Handler、回調函數和攜帶數據等);
●通過Handler的sendMessage()等類似方法將Message發送出去;
●在Handler的處理方法裏面將Message添加到Handler綁定的Looper的MessageQueue;
●Looper的loop()方法通過循環不斷從MessageQueue裏面提取Message進行處理,並移除處理完畢的Message;
●通過調用Message綁定的Handler對象的dispatchMessage()方法完成對消息的處理。
我們可以用下圖來表示android的消息模型
圖2.2 Android消息模型