主要參考文獻(xiàn):深入理解Android,卷1
Android 5.0 源碼
學(xué)識(shí)尚淺,錯(cuò)誤之處請(qǐng)指正俄认。
為什么需要這種機(jī)制?
<p>在Android的應(yīng)用開(kāi)發(fā)中我們經(jīng)常遇到這樣的場(chǎng)景碍脏,為了避免在UI線程中的耗時(shí)操作產(chǎn)生ANR,我們會(huì)開(kāi)啟新的線程來(lái)處理好事操作梭依,得到處理結(jié)果在對(duì)UI進(jìn)行更新。
但是因?yàn)閍ndroid本身設(shè)計(jì)時(shí)考慮到性能優(yōu)先典尾,所以UI操作并非線程安全的,為了避免多線程操作UI帶來(lái)的線程安全問(wèn)題糊探,android設(shè)計(jì)者直接規(guī)定:只允許UI線程來(lái)操作UI組件钾埂。
為了解決這個(gè)問(wèn)題我們需要考慮這么幾個(gè)問(wèn)題:
- 解決線程間的相互通信。
- 一方發(fā)送的消息另一方能夠及時(shí)收到科平,并做出處理褥紫。
Handler異步消息處理機(jī)制就是Android中解決這個(gè)問(wèn)題提供的一種方案。
基本的原理很簡(jiǎn)單:
<p>
- 一個(gè)消息隊(duì)列:可以往其中添加消息瞪慧。
- 一個(gè)消息循環(huán):持續(xù)不斷的從消息隊(duì)列中取出消息髓考,進(jìn)行處理。
<p>
他的四個(gè)組成部分Message弃酌、Handler氨菇、MessageQueue和Looper儡炼,形象的說(shuō)明了各自的分工:
- Message:封裝需要傳遞的消息
- Handler:發(fā)送和處理消息
- MessageQueue:消息隊(duì)列,采用先進(jìn)先出的方式管理Message
- Looper:他負(fù)責(zé)管理MessageQueue查蓉,不斷的從中讀取消息乌询,并將讀到的消息交給Handler處理。
舉一個(gè)并不確切的例子說(shuō)一下我的理解:
Handler好比一個(gè)要搬家的人豌研,不同的線程就像是舊的住所和新的住所妹田,你在舊的住所將自己的東西打包好裝在行李箱(Message )中,并在上面貼上自己的標(biāo)簽,如姓名聯(lián)系方式等(Message.target);MessageQueque好比是物流鹃共,你將打包好的行李箱交給他鬼佣,他幫你運(yùn)輸和存儲(chǔ);looper就好比送貨員,他將運(yùn)送過(guò)來(lái)的消息取出來(lái)霜浴,查看主人(target),它將你的東西送到你的手里晶衷,任憑你的處理和擺放。
如何簡(jiǎn)單地使用它
<p>網(wǎng)上有很多這樣的教程坷随,但是我看了一些房铭,覺(jué)得大多數(shù)單單使用這一點(diǎn)并沒(méi)有講清楚,讓初次接觸的人用起來(lái)非常復(fù)雜温眉,其實(shí)事實(shí)并不是這樣的缸匪。希望我能夠嘗試這說(shuō)清楚。
首先它是一種線程間通信的通信的方式类溢,因此至少涉及到了兩個(gè)線程凌蔬,我們將這兩個(gè)線程按照一次消息處理的地位分為發(fā)送消息線程和接受消息線程。
對(duì)于接受線程:
- 創(chuàng)建Looper對(duì)象闯冷。
Looper.prepare();使用Looper的prepare()方法即可創(chuàng)建一個(gè)Looper對(duì)象砂心,其中也包含了MessageQueque對(duì)象的創(chuàng)建,當(dāng)然每一個(gè)線程中只能允許擁有一個(gè)Looper對(duì)象蛇耀。 - 初始化Handler對(duì)象辩诞,并重寫(xiě)其handleMessage()方法來(lái)處理接受的消息。
- 啟動(dòng)Looper纺涤。
Looper.loop();使用Looper的loop()方法即可啟動(dòng)loop,這樣Looper對(duì)象才能不斷的管理MessageQueque译暂。
對(duì)于發(fā)送線程:
- 創(chuàng)建攜帶信息的Message對(duì)象。
- 使用接受線程創(chuàng)建的Handler對(duì)象來(lái)發(fā)送消息撩炊。
reThread.mHandler.sendMessage(msg);
簡(jiǎn)單的小程序
/**
* 該例子是在子線程為發(fā)送消息的線程外永,主線程中接收消息
*/
public class HandlerTestActivity extends AppCompatActivity {
//聲明Handler變量
private Handler mHandler ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
//開(kāi)啟一個(gè)接受線程
Thread reThread = new Thread(){
@Override
public void run(){
//1.使用prepare函數(shù)創(chuàng)建Looper對(duì)象
Looper.prepare();
//2.初始化Handler對(duì)象
mHandler = new Handler(){
//3.重寫(xiě)handleMessage方法來(lái)處理接收到的消息
@Override
public void handleMessage(Message msg) {
//Message.what字段可以攜帶少量信息,這里作為身份識(shí)別信息
if(msg.what==0x123){
//接收到消息拧咳,彈出對(duì)話框提醒
Toast.makeText(HandlerTestActivity.this,"reThread接收到消息",Toast.LENGTH_LONG).show();
}
}
};
//3.使用loop函數(shù)開(kāi)啟Looper
Looper.loop();
}
};
//開(kāi)啟該接收消息的線程
reThread.start();
}
/**
* 添加一個(gè)按鈕伯顶,為按鈕添加點(diǎn)擊事件
* 在該回調(diào)函數(shù)中向接收線程發(fā)送消息
*/
public void sendMessageFromSendThread(View view){
//1.創(chuàng)建Message對(duì)象
Message sendMsg = new Message();
//在Message的what字段中攜帶少量信息,用于消息的身份識(shí)別
sendMsg.what = 0x123;
//使用接收線程創(chuàng)建的Handler對(duì)象的sendMessage函數(shù)向接收線程發(fā)送消息
mHandler.sendMessage(sendMsg);
}
}
這里有一個(gè)需要注意的問(wèn)題:
我們經(jīng)常使用的場(chǎng)景中是主線程即UI線程最為接收消息的線程,然后更新UI來(lái)相應(yīng)數(shù)據(jù)的變化祭衩,在這種情況下灶体,Looper對(duì)象的創(chuàng)建和啟動(dòng)這兩個(gè)步驟可以省略,因?yàn)閍ndroid本身已經(jīng)默認(rèn)為主線程完成了Looper相關(guān)的工作汪厨,用戶只要實(shí)現(xiàn)Handler相關(guān)的即可赃春。具體我們后面再做分析。
源碼分析
<p>
1.Looper類
主要變量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
Looper構(gòu)造器聲明為private劫乱,不允許用戶自己初始化Looper對(duì)象;自身攜帶了消息隊(duì)列MessageQueue织中。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
用戶需要調(diào)用prepare函數(shù)來(lái)創(chuàng)建對(duì)象,可以看出prepare函數(shù)保證了一個(gè)線程中只有一個(gè)looper對(duì)象也就意味著只有一個(gè)MessageQueue消息隊(duì)列衷戈。除此之外prepare函數(shù)還利用線程的ThreadLocal變量來(lái)存儲(chǔ)該線程創(chuàng)建的Looper對(duì)象狭吼,使用這種機(jī)制來(lái)關(guān)聯(lián)looper對(duì)象和他的調(diào)用線程。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//ThreadLocal為線程局部變量類
//set方法設(shè)置線程的局部變量
//get方法得到線程的局部變量
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
loop函數(shù)開(kāi)啟了一個(gè)死循環(huán)殖妇,不斷的從消息隊(duì)列中讀取消息刁笙,并將消息交給Handler對(duì)象去處理。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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;
//略去一部分
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//分發(fā)給message的target字段包含的目標(biāo)handler中谦趣。
msg.target.dispatchMessage(msg);
//略去一部分
}
}
public static Looper myLooper() {
return sThreadLocal.get();
}
總結(jié)Looper的作用:
- 封裝了一個(gè)消息隊(duì)列
- 將Looper對(duì)象和調(diào)用prepare函數(shù)的線程綁定
- 保證了每一個(gè)線程只能有一個(gè)looper對(duì)象和消息隊(duì)列
- 及時(shí)取出消息隊(duì)列中的消息將他交給綁定的線程中的handler進(jìn)行處理疲吸。
2.Handler類
看源碼中說(shuō)明:
A Handler allows you to send and process Message and Runnable
objects associated with a thread's MessageQueue. Each Handler
instance is associated with a single thread and that thread's message
queue. When you create a new Handler, it is bound to the thread /
message queue of the thread that is creating it -- from that point on,
it will deliver messages and runnables to that message queue and execute
them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and
runnables to be executed as some point in the future; and (2) to enqueue
an action to be performed on a different thread than your own.
Handler構(gòu)造器有很多個(gè),最終會(huì)調(diào)用到這兩個(gè)前鹅。
//沒(méi)有傳入looper對(duì)象時(shí)摘悴,使用Looper.myLooper()獲得,前面提到過(guò)myLooper函數(shù)是從當(dāng)前線程中取出綁定的looper對(duì)象舰绘。
//然后獲得該looper對(duì)象中的消息隊(duì)列
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
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 = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
sendMessage相關(guān)的幾個(gè)函數(shù)蹂喻,最終會(huì)調(diào)用sendMessageAtTime這個(gè)函數(shù)。他把得到的消息Message對(duì)象和消息隊(duì)列MessageQueue交給enqueueMessage函數(shù)處理捂寿。
enqueueMessage負(fù)責(zé)在消息Message的target字段標(biāo)記為自己口四,以便接收到的消息能夠分發(fā)到自己。
接下來(lái)交給MessageQueque的enqueueMessage()方法將消息添加到消息隊(duì)列中秦陋。
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);
}
前面Looper對(duì)象取出消息隊(duì)列中的消息后會(huì)根據(jù)msg.target調(diào)用該Handler的dispatchMessage函數(shù)蔓彩。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
//如果msg本身包含callback,交給消息的callback去處理
if (msg.callback != null) {
handleCallback(msg);
} else {//如果沒(méi)有驳概,講給Handler的callback去處理粪小,也就是重寫(xiě)的handleMessage函數(shù)去處理。
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
總結(jié)Handler的作用:
- 封裝了將Message添加到MessageQueque的過(guò)程抡句,即發(fā)送消息過(guò)程
- 在發(fā)送的消息中打上自己的標(biāo)簽
- 處理自己的發(fā)送的消息
MessageQueue和Message這兩個(gè)組成部分比較簡(jiǎn)單,在此不做詳解杠愧〈疲看完了以上的代碼部分的分析,知道這個(gè)機(jī)制原理很簡(jiǎn)單,這樣再回去看看我那個(gè)不準(zhǔn)確的小例子锐锣,你會(huì)發(fā)現(xiàn)很多的不合理的之處腌闯,但是用來(lái)初步理解還是可以的。