首先要知道,android 中有兩個重要的機制,一個是Binder機制,另一個是handler機制.
為什麼要引入handler機制呢,因為在android中,主線程不能進行耗時的操作,否則會發生ANR異常.子線程做耗時的操作,如聯網操作,i/o操作,做完耗時操作後可能需要更新UI,但是android不允許在子線程中更新UI,否則就會觸發程式異常,這個時候就可以通過Handler+子線程将更新UI的操作切換到主線程中進行.
- 為什麼不可以在子線程中更新UI
- 因為android的UI線程不是線程安全的(就是指如果多個線程調用這個類的方法,會出現不可預知的不正常的情況,那麼線程安全的意思就是多個線程同時操作不會有問題),如果多個子線程并發發通路(同時通路)會導緻UI空間處于不可預期的狀态
- UI空間不可以枷鎖,因為一會導緻UI通路的邏輯變得複雜,其次會降低UI通路的效率,會阻塞某些線程的執行’
2.消息機制原理分析
1. 核心類
* message: 可分為硬體産生的消息和軟體産生的消息,一個handler可以發送多個消息
* messageQueue:消息隊列,主要用來存放handler發送過來的消息,由系統維護,隻有一個執行個體
* handler:消息輔助類,主要通過sendMessage()發送消息,通過handlerMessage()接受消息,每個activity都可以new一個handler,是以可以有多個handler
* looper:輪詢器 ,不斷從消息隊列中輪詢,取出消息,當沒有消息的時候回休眠,同時還負責分發消息
首先需要在主線程中建立一個Handler對象,并重寫Handler.Message(),并調用sendMessage(),将之存到MessageQueue中,而Looper則會一直嘗試從MessageQueue中取出待處理消息,最後分發回handleMessage()中,這是一個無限循環。Looper若沒有新消息需要處理則會進入等待狀态,直到有消息需要處理為止。
2.Message,handler,MessageQueue,Looper對象的建立
2.1 message的建立
有三種方式:
1.new Message()
2.message.obtain()
3.handler.obtainMessage() 内部調用了message.obtain()
源碼:
//message.obtain()
public static Message obtain() {
synchronized (mPoolSync) {
if (mPool != null) {
Message m = mPool;
mPool = m.next;
m.next = null;
return m;
}
}
return new Message();
}
//Handler中的obtainMessage(),本質上還是調用了Message.obtain(this)
public final Message obtainMessage()
{
return Message.obtain(this);//this其實就是handler執行個體
}
2.2 handler looper messagequeue的建立
new handler();
一個應用中可以同時有多個handler對象,一個handler對象也可以發送多個message
handler相關源碼:
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == ) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
//looper中的相關源碼
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());//looper就是在這裡被執行個體化的,一定要注意
}
public static final void prepareMainLooper() {
prepare();//調用了prepare(),
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
//ActivityThread中的源碼
public static final void main(String[] args) {
SamplingProfilerIntegration.start();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();//在這裡調用了prepareMainLooper()
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
//在looper的構造方法中建立了Messagequeue,looper的構造又是在prepare()被調用
private Looper() {
mQueue = new MessageQueue();//建立Messagequeue
mRun = true;
mThread = Thread.currentThread();
}
2.3 handler是如何發送消息的
handler依次調用以下3個方法:
sendMessage(Message msg)
sendMessageDelayed(Message msg, long delayMillis)
endMessageAtTime(Message msg, long uptimeMillis)
具體源碼如下:
//調用了 sendMessageDelayed(Message msg, long delayMillis)
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, );
}
//endMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < ) {
delayMillis = ;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);//手機開機時間,加上延遲發送的時間
//通過調用enqueueMessage(msg, uptimeMillis)将消息插入消息隊列
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);//将消息插入隊列
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
2.4 messagequeue插入消息的原理
主要由 enqueueMessage(Message msg, long when)方法實作
情形一:當隊列中沒有消息,或者要插入的消息比隊列中的第一消息還要早執行
final boolean enqueueMessage(Message msg, long when) {
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages; //mMessages表示隊列中的第一個消息
if (p == null || when == || when < p.when) {
//message的next屬性指向的是下一個消息
msg.next = p;//原來隊列第一個消息變為第二個消息
mMessages = msg;//将要插入的消息變為第一個消息
needWake = mBlocked; // new head, might need to wake up
} else {
情形二:需要将消息插入到中間的情況
else {
Message prev = null;//prev中間變量
while (p != null && p.when <= when) {
/**通過while循環不斷判斷,如果要插入的消息比原來第一個晚執行
/*
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
情形二的邏輯比較複雜,下假設需要插入到第二個,一定要畫圖進行了解:
插入消息完整的源碼如下:
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != ) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
final boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == || 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;
}
2.5 looper喚醒
looper會一直輪詢messagequeue,當隊列中沒有消息的時候,looper會休眠,當handler發送消息後,它又會被喚醒
hanlder在發送消息的時候,寫入一個标志:W
2.6 looper如何将msg發送給制定的handler對象
msg.sendToTarget(); //發送給handler
msg.sendToTarget(); //發送給handler
public void sendToTarget() {
target.sendMessage(this);//this就是handler,根據不同的handler對象将消息發送給不同的hanglder
}
2.7 looper如何分發消息
首先調用loop(),在loop()中一次調用下邊幾個方法:
Looper me = myLooper();//擷取looper對象
MessageQueue queue = me.mQueue;//找到消息隊列
Message msg = queue.next();//取消息
msg.target.dispatchMessage(msg);//調用handler的 dispatchMessage(msg)方法
handler中的 dispatchmessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);//當msg中傳有回調時執行此方法
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {//mCallback 表示handler中自己的回調
return;
}
}
handleMessage(msg);//調用handleMessage() 具體是有我們自己實作
}
}
注:handler移除消息 有待研究
總結:
其實Asychtask内部也是使用了handler,從Android 1.5開始系統将AsyncTask引入到android.os包中,過去在很早1.1和1.0 SDK時其實官方将其命名為UserTask,其内部是JDK 1.5開始新增的concurrent庫,做過J2EE的網友可能明白并發庫效率和強大性,比Java原始的Thread更靈活和強大,但對于輕量級的使 用更為占用系統資源。Thread是Java早期為實作多線程而設計的,比較簡單不支援concurrent中很多特性在同步和線程池類中需要自己去實作 很多的東西,對于分布式應用來說更需要自己寫排程代碼,而為了Android UI的重新整理Google引入了Handler和Looper機制,它們均基于消息實作,有時可能消息隊列阻塞或其他原因無法準确的使用。
推薦大家使用AsyncTask代替Thread+Handler的方式,不僅調用上更為簡單,經過實測更可靠一些,Google在Browser中大量 使用了異步任務作為處理耗時的I/O操作,比如下載下傳檔案、讀寫資料庫等等,它們在本質上都離不開消息,但是AsyncTask相比Thread加 Handler更為可靠,更易于維護,但AsyncTask缺點也是有的比如一旦線程開啟即dobackground方法執行後無法給線程發送消息,僅能 通過預先設定好的标記來控制邏輯,當然可以通過線程的挂起等待标志位的改變來通訊,對于某些應用Thread和Handler以及Looper可能更靈 活。
(1)發送消息
Handler支援2種消息類型,即Runnable和Message。是以發送消息提供了post(Runnable r)和sendMessage(Message msg)兩個方法。從下面源碼可以看出Runnable指派給了Message的callback,最終也是封裝成Message對象對象。學姐個人認為外部調用不統一使用Message,應該是相容Java的線程任務,學姐認為這種思想也可以借鑒到平常開發過程中。發送的消息都會入隊到MessageQueue隊列中。
(2)處理消息
Looper循環過程的時候,是通過dispatchMessage(Message msg)對消息進行處理。處理過程:先看是否是Runnable對象,如果是則調用handleCallback(msg)進行處理,最終調到Runnable.run()方法執行線程;如果不是Runnable對象,再看外部是否傳入了Callback處理機制,若有則使用外部Callback進行處理;若既不是Runnable對象也沒有外部Callback,則調用handleMessage(msg),這個也是我們開發過程中最常覆寫的方法了。
(3)移除消息
removeCallbacksAndMessages(),移除消息其實也是從>>MessageQueue中将Message對象移除掉。