Hanlder的使用
handler有兩種使用方式屋讶,下面開(kāi)始介紹
方式一:使用Handler.sendMessage()
在該方式中聪廉,又可以分為兩種:新建Handler子類(內(nèi)部類)娇妓、匿名 Handler子類刑巧。
/**
* 方式一:新建Handler子類(內(nèi)部類)
*/
// 步驟1:自定義Handler子類(繼承Handler類),復(fù)寫handleMessage()方法
class mHandler extends Handler {
// 通過(guò)復(fù)寫handlerMessage() 從而確定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需執(zhí)行的UI操作
}
}
// 步驟2:在主線程中創(chuàng)建Handler實(shí)例
private Handler mhandler = new mHandler();
// 步驟3:創(chuàng)建所需的消息對(duì)象
Message msg = Message.obtain(); // 實(shí)例化消息對(duì)象
msg.what = 1; // 消息標(biāo)識(shí)
msg.obj = "AA"; // 消息內(nèi)容存放
// 步驟4:在工作線程中 通過(guò)Handler發(fā)送消息到消息隊(duì)列中
// 可通過(guò)sendMessage() / post()
// 多線程可采用AsyncTask搂蜓、繼承Thread類、實(shí)現(xiàn)Runnable
mHandler.sendMessage(msg);
mHandler.sendEmptyMessage(0); // 發(fā)送空消息
mHandler.sendMessageDelayed(message,1000); //延遲1000ms后發(fā)送攜帶數(shù)據(jù)的消息
/**
* 方式2:匿名內(nèi)部類
*/
// 步驟1:在主線程中 通過(guò)匿名內(nèi)部類 創(chuàng)建Handler類對(duì)象
private Handler mhandler = new Handler(){
// 通過(guò)復(fù)寫handlerMessage()從而確定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需執(zhí)行的UI操作
}
};
// 步驟2:創(chuàng)建消息對(duì)象
Message msg = Message.obtain(); // 實(shí)例化消息對(duì)象
msg.what = 1; // 消息標(biāo)識(shí)
msg.obj = "AA"; // 消息內(nèi)容存放
// 步驟3:在工作線程中 通過(guò)Handler發(fā)送消息到消息隊(duì)列中
// 多線程可采用AsyncTask由蘑、繼承Thread類闽寡、實(shí)現(xiàn)Runnable
mHandler.sendMessage(msg);
mHandler.sendEmptyMessage(0); // 發(fā)送空消息
mHandler.sendMessageDelayed(message,1000); //延遲1000ms后發(fā)送攜帶數(shù)據(jù)的消息
方式二:使用Handler.post()
// 步驟1:在主線程中創(chuàng)建Handler實(shí)例
private Handler mhandler = new mHandler();
// 步驟2:在工作線程中 發(fā)送消息到消息隊(duì)列中 & 指定操作UI內(nèi)容
// 需傳入1個(gè)Runnable對(duì)象
mHandler.post(new Runnable() {
@Override
public void run() {
... // 需執(zhí)行的UI操作
}
});
Hanlder源碼分析
Handler原理解析
主要內(nèi)容
Handler的消息機(jī)制主要包含以下內(nèi)容:
- Message:消息
- MessageQueue:消息隊(duì)列
- Handler:消息管理類
- Looper:消息循環(huán)類
Handler架構(gòu)圖
Handler的架構(gòu)圖如下:
創(chuàng)建Handler對(duì)象
具體使用
private Handler mhandler = new Handler(){
// 通過(guò)復(fù)寫handlerMessage()從而確定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需執(zhí)行的UI操作
}
};
構(gòu)造方法
Handler構(gòu)造方法,調(diào)用了另一個(gè)構(gòu)造方法尼酿,this(null, false)爷狈。
public Handler() {
this(null, false);
}
幾點(diǎn)說(shuō)明:
- Handler需要綁定線程才能使用;綁定后裳擎,Handler的消息處理會(huì)在綁定的線程中執(zhí)行涎永。
- 綁定方式:指定Looper對(duì)象,從而綁定了Looper對(duì)象所在的線程句惯,因?yàn)長(zhǎng)ooper對(duì)象本已綁定了對(duì)應(yīng)線程土辩。
- 指定了Handler對(duì)象的Looper對(duì)象 = 綁定到了Looper對(duì)象所在的線程支救。
···
public Handler(Callback callback, boolean async) {
.....//省略代碼
//Looper.myLooper()作用:獲取當(dāng)前線程的Looper對(duì)象抢野;若線程無(wú)Looper對(duì)象則拋出異常
//即:若線程中無(wú)創(chuàng)建Looper對(duì)象,則也無(wú)法創(chuàng)建Handler對(duì)象
//故若需在子線程中創(chuàng)建Handler對(duì)象各墨,則需先創(chuàng)建Looper對(duì)象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 綁定消息隊(duì)列對(duì)象(MessageQueue)
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
//至此指孤,保證了handler對(duì)象的MessageQueue關(guān)聯(lián)上Looper對(duì)象中MessageQueue
}
···
從上面可以看出:
- 當(dāng)創(chuàng)建Handler對(duì)象時(shí),通過(guò)構(gòu)造方法自動(dòng)關(guān)聯(lián)當(dāng)前線程的Looper對(duì)象和對(duì)應(yīng)的消息隊(duì)列對(duì)象(MessageQueue),從而自動(dòng)綁定了實(shí)現(xiàn)創(chuàng)建Handler對(duì)象操作的線程恃轩。
那么問(wèn)題來(lái)了结洼,到目前為止,我們不曾創(chuàng)建過(guò)Handler需要關(guān)聯(lián)的Looper對(duì)象和MessageQueue對(duì)象叉跛,那當(dāng)前線程的Looper對(duì)象和對(duì)應(yīng)的消息隊(duì)列的MessageQueue是什么時(shí)候創(chuàng)建的呢松忍?
創(chuàng)建循環(huán)對(duì)象Looper和消息隊(duì)列對(duì)象MessageQueue
- 創(chuàng)建Looper對(duì)象的方法:Looper.prepareMainLooper()和Looper.prepare()。
- Looper.prepareMainLooper():為主線程(UI線程)創(chuàng)建一個(gè)Looper對(duì)象筷厘。
- Looper.prepare()為當(dāng)前線程創(chuàng)建一個(gè)Looper對(duì)象
源碼分析1:Looper.prepareMainLooper()
在Android應(yīng)用進(jìn)程啟動(dòng)時(shí)鸣峭,會(huì)默認(rèn)創(chuàng)建1個(gè)主線程ActivityThread,也叫UI線程酥艳,創(chuàng)建時(shí)摊溶,會(huì)自動(dòng)調(diào)用ActivityThread的靜態(tài)的main()方法 = 應(yīng)用程序的入口,main()內(nèi)則會(huì)調(diào)用Looper.prepareMainLooper()為主線程生成1個(gè)Looper對(duì)象充石,同時(shí)生成一個(gè)消息隊(duì)列對(duì)象MessageQueue莫换。源碼如下:
public static void main(String[] args) {
../// 省略代碼
// 1
Looper.prepareMainLooper();
..///省略代碼
// 創(chuàng)建主線程
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
..///省略代碼
// 開(kāi)啟消息循環(huán),后面分析
Looper.loop();
}
// 1處為主線程創(chuàng)建1個(gè)Looper對(duì)象骤铃,同時(shí)生成1個(gè)消息隊(duì)列對(duì)象MessageQueue拉岁,繼續(xù)看prepareMainLooper(),
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 設(shè)置sMainLooper惰爬,sMainLooper是主線程的Looper膛薛,可以通過(guò)getMainLooper()獲取
sMainLooper = myLooper();
}
}
可以看到,Looper.prepareMainLooper()內(nèi)部其實(shí)是調(diào)用了 prepare()方法的补鼻。
源碼分析2:prepare()方法
prepare()的作用是為當(dāng)前線程創(chuàng)建1個(gè)循環(huán)器對(duì)象(Looper)哄啄,同時(shí)也生成了1個(gè)消息隊(duì)列對(duì)象(MessageQueue)。
注意风范,如果要在子線程中創(chuàng)建Looper對(duì)象咨跌,則需在子線程中手動(dòng)調(diào)用該方法。
private static void prepare(boolean quitAllowed) {
// 判斷sThreadLocal是否為null硼婿,否則拋出異常
// Looper.prepare()方法不能被調(diào)用兩次 = 1個(gè)線程中只能對(duì)應(yīng)1個(gè)Looper實(shí)例
// sThreadLocal = 1個(gè)ThreadLocal對(duì)象锌半,用于存儲(chǔ)線程的變量
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 若為初次Looper.prepare(),則創(chuàng)建Looper對(duì)象 & 存放在ThreadLocal變量中
// Looper對(duì)象是存放在Thread線程里的
sThreadLocal.set(new Looper(quitAllowed));
}
這里需要說(shuō)明一下寇漫,是用ThreadLocal保存Looper對(duì)象刊殉。- Handler通過(guò)綁定Looper對(duì)象的方式綁定到當(dāng)前線程。
- 一個(gè)線程對(duì)應(yīng)著一個(gè)Looper州胳。
- 一個(gè)Looper對(duì)應(yīng)著一個(gè)MessageQueue记焊。
- 線程默認(rèn)是沒(méi)有Looper的,線程需要通過(guò)Looper.prepare()栓撞、綁定Handler到Looper對(duì)象遍膜、Looper.loop()來(lái)建立消息循環(huán)碗硬。
- 主線程(UI線程),也就是ActivityThread瓢颅,在被創(chuàng)建的時(shí)候就會(huì)初始化Looper恩尾,所以主線程中可以默認(rèn)使用Handler。
綜上所述:Threadlocal是一個(gè)線程內(nèi)部的存儲(chǔ)類挽懦,可以在指定線程內(nèi)存儲(chǔ)數(shù)據(jù)翰意,數(shù)據(jù)存儲(chǔ)以后,只有指定線程可以得到存儲(chǔ)數(shù)據(jù)信柿。ThreadLocal的作用是保證一個(gè)線程對(duì)應(yīng)一個(gè)Looper猎物,同時(shí)各個(gè)線程之間的Looper互不干擾。那么ThreadLocal是怎么保證的呢角塑?
實(shí)際上ThreadLocal通過(guò)為每個(gè)線程提供一個(gè)獨(dú)立的變量副本解決了變量并發(fā)訪問(wèn)的沖突問(wèn)題蔫磨,ThreadLocal為解決多線程程序的并發(fā)問(wèn)題提供了一種新的思路。這里再多提一下圃伶,ThreadLocal和Synchronized都是為了解決多線程中相同變量的訪問(wèn)沖突問(wèn)題堤如,不同的是,
- Synchronized是通過(guò)線程等待窒朋,犧牲時(shí)間來(lái)解決訪問(wèn)沖突
- ThreadLocal是通過(guò)每個(gè)線程單獨(dú)一份存儲(chǔ)空間搀罢,犧牲空間來(lái)解決沖突
源碼分析3:Looper的構(gòu)造方法
以上在prepare()方法中,創(chuàng)建Looper對(duì)象并存放在sThreadLocal中侥猩,下面查看Looper的構(gòu)造方法榔至。
private Looper(boolean quitAllowed) {
// 創(chuàng)建1個(gè)消息隊(duì)列對(duì)象(MessageQueue)
// 即 當(dāng)創(chuàng)建1個(gè)Looper實(shí)例時(shí),會(huì)自動(dòng)創(chuàng)建一個(gè)與之配對(duì)的消息隊(duì)列對(duì)象(MessageQueue)
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到欺劳,在創(chuàng)建Looper對(duì)象的時(shí)候唧取,創(chuàng)建了MessageQueue對(duì)象。
即一個(gè)Looper對(duì)應(yīng)著一個(gè)MessageQueue划提。
==根據(jù)以上總結(jié)==:
-
創(chuàng)建主線程時(shí)枫弟,會(huì)自動(dòng)調(diào)用ActivityThread的1個(gè)靜態(tài)的main();而main()內(nèi)則會(huì)調(diào)用Looper.prepareMainLooper()為主線程生成1個(gè)Looper對(duì)象鹏往,同時(shí)也會(huì)生成其對(duì)應(yīng)的MessageQueue對(duì)象淡诗。
- 即 主線程的Looper對(duì)象自動(dòng)生成,不需手動(dòng)生成伊履;而子線程的Looper對(duì)象則需手動(dòng)通過(guò)Looper.prepare()創(chuàng)建韩容。
- 在子線程若不手動(dòng)創(chuàng)建Looper對(duì)象 則無(wú)法生成Handler對(duì)象。
根據(jù)Handler的作用(在主線程更新UI)唐瀑,故Handler實(shí)例的創(chuàng)建場(chǎng)景 主要在主線程群凶。
生成Looper & MessageQueue對(duì)象后,則會(huì)自動(dòng)進(jìn)入消息循環(huán):Looper.loop()介褥。
消息循環(huán)Looper.loop()
Looper.loop()主要是消息循環(huán)座掘,從消息隊(duì)列中獲取消息,分發(fā)消息到Handler中柔滔。
public static void loop() {
// --- 1.獲取當(dāng)前Looper的消息隊(duì)列MessageQueue -----
// 第一步
// 獲取當(dāng)前Looper對(duì)象
final Looper me = myLooper();
// myLooper()的作用是返回sThreadLocal存儲(chǔ)的Looper實(shí)例
// 若me為null溢陪,則拋出異常
// 所以在執(zhí)行l(wèi)oop()方法之前,必須執(zhí)行prepare()方法睛廊,prepare() //的作用是創(chuàng)建Looper實(shí)例
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 第二步
// 獲取Looper實(shí)例中的消息隊(duì)列對(duì)象MessageQueue
final MessageQueue queue = me.mQueue;
// ......代碼省略
//------ 2. 消息循環(huán)形真,無(wú)限循環(huán) --------------
// 第三步
for (;;) {
// 從MessageQueue中取出消息
// 第四步
Message msg = queue.next(); // might block
// 如果消息為空,則退出循環(huán)
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ......代碼省略
final long dispatchEnd;
try {
// 第五步
// 分發(fā)消息到對(duì)應(yīng)的Handler
// 把消息派發(fā)到msg的target屬性
//target屬性實(shí)際上是一個(gè)handler對(duì)象
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// ......代碼省略
// 第六步
// 回收消息
msg.recycleUnchecked();
}
}
消息循環(huán)Looper.loop()超全,主要作用的取出消息咆霜,通過(guò)以上代碼分析,大致分為六步:
- 第一步嘶朱,獲取Looper對(duì)象
- 第二步蛾坯,獲取Looper實(shí)例中的消息隊(duì)列對(duì)象MessageQueue
- 第三步,while()無(wú)限循環(huán)遍歷
- 第四步疏遏,通過(guò)queue.next()從MessageQueue中取出一個(gè)Message對(duì)象
- 第五步脉课,通過(guò)msg.target.dispatchMessage(msg)來(lái)處理消息
- 第六步,通過(guò) msg.recycleUnchecked()來(lái)回收消息
其中第四财异,五倘零,六三步涉及到消息的取出和消息的處理,在后面介紹戳寸。
消息的發(fā)送呈驶、取出和處理
對(duì)于消息的發(fā)送,取出和處理疫鹊,參照下面的文章袖瞻。
總結(jié)
最后再總結(jié)一下,Handler工作的流程圖: