Handler的由來
當(dāng)程序第一次啟動(dòng)的時(shí)候,Android會(huì)同時(shí)啟動(dòng)一條主線程(Main Thread)來負(fù)責(zé)處理與UI相關(guān)的事件纹坐,我們叫做UI線程枝冀。
Android的UI操作并不是線程安全的(出于性能優(yōu)化考慮),意味著如果多個(gè)線程并發(fā)操作UI線程,可能導(dǎo)致線程安全問題宾茂。
為了解決Android應(yīng)用多線程問題——Android平臺(tái)只允許U線程修改Activity里的UI組件瓷马,就會(huì)導(dǎo)致新啟動(dòng)的線程無法改變界面組件的屬性值。
簡單總結(jié):當(dāng)主線程隊(duì)列處理一個(gè)消息超過5秒跨晴,android就會(huì)拋出一個(gè)ANR(無響應(yīng))的異常欧聘,所以,我們需要把一些要處理時(shí)間比較長的消息端盆,放在一個(gè)單獨(dú)線程里面進(jìn)行處理怀骤,把處理以后的結(jié)果,返回給主線程運(yùn)行焕妙,就需要用到Handler來進(jìn)行線程組件的通信蒋伦。
Handler的作用
- 讓線程延時(shí)執(zhí)行,主要用到兩個(gè)方法:
方法1:final boolean postAtTime(Runnable r, long uptimeMillis)
方法2:final boolean postDelayed(Runnable r, long delayMillis)```
* 讓任務(wù)在其他線程中執(zhí)行并返回結(jié)果焚鹊,分為兩個(gè)步驟:
**1.在新啟動(dòng)的線程中發(fā)送消息**
使用Handler對(duì)象的```sendMessage()```方法或者```sendEmptyMessage()```方法發(fā)送消息痕届。
**2.在主線程中獲取處理消息**
重寫Handler類中處理消息的方法(void handleMessage(Message msg)),當(dāng)新啟動(dòng)的線程發(fā)送消息時(shí),消息發(fā)送到與之關(guān)聯(lián)的MessageQueue末患。而Handler不斷地從MessageQueue中獲取并處理消息研叫。
#####Handler更新UI線程一般使用
(1):首先要進(jìn)行Handler聲明,復(fù)用handleMessage方法(放在主線程中)
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO 接收消息并且去更新UI線程上的控件內(nèi)容
if (msg.what == UPDATE) {
// 更新界面上的textview
tv.setText(String.valueOf(msg.obj));
}
super.handleMessage(msg);
}
};```
(2):子線程發(fā)送Message給UI線程表示自己任務(wù)已經(jīng)執(zhí)行完成璧针,主線程可以做相應(yīng)的操作了嚷炉。
new Thread() {
@Override
public void run() {
// TODO 子線程中通過handler發(fā)送消息給handler接收
//,由handler去更新TextView的值
try {
//do something
Message msg = new Message();
msg.what = UPDATE;
msg.obj = "更新后的值" ;
handler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();```
#####Handler原理分析
* **Handler的構(gòu)造函數(shù)**
1: public Handler()
2: public Handler(Callback callback)
3: public Handler(Looper looper)
4: public Handler(Looper looper, Callback callback) ```
第①個(gè)和第②個(gè)構(gòu)造函數(shù)都沒有傳遞Looper探橱,這兩個(gè)構(gòu)造函數(shù)都將通過調(diào)用Looper.myLooper()獲取當(dāng)前線程綁定的Looper對(duì)象申屹,然后將該Looper對(duì)象保存到名為mLooper的成員字段中。
下面來看①②個(gè)函數(shù)源碼:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
//他們會(huì)調(diào)用Handler的內(nèi)部構(gòu)造方法
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());
}
}
//重點(diǎn):Looper.myLooper()獲取了當(dāng)前線程保存的Looper實(shí)例
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not
called Looper.prepare()");
}
//重點(diǎn):獲取MessageQueue(消息隊(duì)列)
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}```
**通過Looper.myLooper()獲取了當(dāng)前線程保存的Looper實(shí)例隧膏,又通過這個(gè)Looper實(shí)例獲取了其中保存的MessageQueue(消息隊(duì)列)哗讥。每個(gè)Handler對(duì)應(yīng)一個(gè)Looper對(duì)象,產(chǎn)生一個(gè)MessageQueue**
第③個(gè)和第④個(gè)構(gòu)造函數(shù)傳遞了Looper對(duì)象胞枕,這兩個(gè)構(gòu)造函數(shù)會(huì)將該Looper保存到名為mLooper的成員字段中杆煞。
下面來看③④個(gè)函數(shù)源碼:
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
//他們會(huì)調(diào)用Handler的內(nèi)部構(gòu)造方法
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
第②個(gè)和第④個(gè)構(gòu)造函數(shù)還傳遞了Callback對(duì)象,Callback是Handler中的內(nèi)部接口曲稼,需要實(shí)現(xiàn)其內(nèi)部的handleMessage方法索绪,Callback代碼如下:
public interface Callback {
public boolean More ...handleMessage(Message msg);
}```
Handler.Callback是用來處理Message的一種手段,如果沒有傳遞該參數(shù)贫悄,那么就應(yīng)該重寫Handler的handleMessage方法瑞驱,也就是說為了使得Handler能夠處理Message,我們有兩種辦法:
方法1:向Hanlder的構(gòu)造函數(shù)傳入一個(gè)Handler.Callback對(duì)象窄坦,并實(shí)現(xiàn)Handler.Callback的handleMessage方法
方法2:無需向Hanlder的構(gòu)造函數(shù)傳入Handler.Callback對(duì)象唤反,但是需要重寫Handler本身的handleMessage方法
也就是說無論哪種方式凳寺,我們都得通過某種方式實(shí)現(xiàn)handleMessage方法,這點(diǎn)與Java中對(duì)Thread的設(shè)計(jì)有異曲同工之處彤侍。
- Handle發(fā)送消息的幾個(gè)方法源碼
//發(fā)送消息
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}```
//發(fā)送空消息肠缨,并且?guī)в袝r(shí)間延遲
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}```
//發(fā)送消息,并且?guī)в袝r(shí)間延遲
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg,
SystemClock.uptimeMillis() + delayMillis);
}```
//發(fā)送消息盏阶,并且?guī)в芯唧w的時(shí)間限定
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);
}```
我們可以看出他們最后都調(diào)用了sendMessageAtTime()晒奕,然后返回了enqueueMessage方法,下面看一下此方法源碼:
private boolean enqueueMessage(MessageQueue queue
, Message msg, long uptimeMillis) {
//把當(dāng)前的handler作為msg的target屬性
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}```
在該方法中有兩件事需要注意:
**1. msg.target = this;**該代碼將Message的target綁定為當(dāng)前的Handler
**2. queue.enqueueMessage **
變量queue表示的是Handler所綁定的消息隊(duì)列MessageQueue名斟,通過queue.enqueueMessage(msg,uptimeMillis)我們將Message放入到消息隊(duì)列中脑慧。
下面通過一張圖,來看完整的方法調(diào)用順序:
![Handler砰盐、Looper闷袒、Message、MessageQueue調(diào)用順序](http://upload-images.jianshu.io/upload_images/3416944-d9d3d3505b4ba800.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#####Looper原理分析
我們一般在主線程聲明Handler岩梳,有時(shí)我們需要繼承Thread類實(shí)現(xiàn)自己的線程功能囊骤,當(dāng)我們?cè)诶锩媛暶鱄andler的時(shí)候會(huì)報(bào)錯(cuò)。其原因是主線程中已經(jīng)實(shí)現(xiàn)了兩個(gè)重要的Looper方法冀值,下面看一看ActivityThread.java中main方法的源碼:
public static void main(String[] args) {
//......省略
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop
unexpectedly exited");
}```
- 首先看prepare()方法
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//證明了一個(gè)線程中只有一個(gè)Looper實(shí)例
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be
created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}```
該方法會(huì)調(diào)用Looper構(gòu)造函數(shù)同時(shí)實(shí)例化出MessageQueue和當(dāng)前thread.
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static MessageQueue myQueue() {
return myLooper().mQueue;
}```
prepare()方法中通過ThreadLocal對(duì)象實(shí)現(xiàn)Looper實(shí)例與線程的綁定也物。(不清楚的可以查看 ThreadLocal的使用規(guī)則和源碼分析)
- 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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
//重點(diǎn)****
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " "
+ msg.callback);
}
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent)
+ " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}```
首先looper對(duì)象不能為空,就是說loop()方法調(diào)用必須在prepare()方法的后面池摧。
Looper一直在不斷的從消息隊(duì)列中通過MessageQueue的next方法獲取Message焦除,然后通過代碼```msg.target.dispatchMessage(msg)```讓該msg所綁定的Handler執(zhí)行dispatchMessage()方法以實(shí)現(xiàn)對(duì)Message的處理激况。
Handler的dispatchMessage的源碼如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}```
我們可以看到Handler提供了三種途徑處理Message作彤,erqie處理有前后優(yōu)先級(jí)之分:首先嘗試讓postXXX()中傳遞的Runable執(zhí)行,其次嘗試讓Handler構(gòu)造函數(shù)中傳入的Callback的handleMessage()方法處理乌逐,最后才是讓Handler自身的handleMessage()方法處理Message竭讳。
如何在子線程中使用Handler
Handler本質(zhì)是從當(dāng)前的線程中獲取到Looper來監(jiān)聽和操作MessageQueue,當(dāng)其他線程執(zhí)行完成后回調(diào)當(dāng)前線程浙踢。
子線程需要先prepare()才能獲取到Looper對(duì)象绢慢,是因?yàn)樽泳€程只是一個(gè)普通的線程,其ThreadLoacl中沒有設(shè)置過Looper洛波,所以會(huì)拋出異常胰舆,而在Looper的prepare()方法中sThreadLocal.set(new Looper())是設(shè)置了Looper的。
- 實(shí)例代碼:定義一個(gè)類實(shí)現(xiàn)Runnable接口或繼承Thread類(一般不繼承)蹬挤。
class Rub implements Runnable {
public Handler myHandler;
// 實(shí)現(xiàn)Runnable接口的線程體
@Override
public void run() {
/*1缚窿、調(diào)用Looper的prepare()方法為當(dāng)前線程創(chuàng)建Looper對(duì)象并,
創(chuàng)建Looper對(duì)象時(shí)焰扳,它的構(gòu)造器會(huì)自動(dòng)的創(chuàng)建相對(duì)應(yīng)的MessageQueue*/
Looper.prepare();
/*2倦零、創(chuàng)建Handler子類的實(shí)例误续,重寫HandleMessage()方法
,該方法處理除當(dāng)前線程以外線程的消息*/
myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String ms = "";
if (msg.what == 0x777) {
}
}
};
//3扫茅、調(diào)用Looper的loop()方法來啟動(dòng)Looper讓消息隊(duì)列轉(zhuǎn)動(dòng)起來
Looper.loop();
}
}```
**注意分成三步:**
1:調(diào)用Looper的prepare()方法為當(dāng)前線程創(chuàng)建Looper對(duì)象蹋嵌,創(chuàng)建Looper對(duì)象時(shí),它的構(gòu)造器會(huì)創(chuàng)建與之配套的MessageQueue葫隙。
2:有了Looper之后栽烂,創(chuàng)建Handler子類實(shí)例,重寫HanderMessage()方法恋脚,該方法負(fù)責(zé)處理來自于其他線程的消息愕鼓。
3:調(diào)用Looper的looper()方法啟動(dòng)Looper。然后使用這個(gè)handler實(shí)例在任何其他線程中發(fā)送消息慧起,最終處理消息的代碼都會(huì)在你創(chuàng)建的Handler實(shí)例的線程中運(yùn)行菇晃。
#####總結(jié)
* **Handler**
發(fā)送消息,它能把消息發(fā)送給Looper管理的MessageQueue蚓挤,Looper分發(fā)給它消息磺送。
* **Message**
Handler接收和處理的消息對(duì)象。
* **Looper**
每個(gè)線程只有一個(gè)Looper灿意,它負(fù)責(zé)管理對(duì)應(yīng)的MessageQueue估灿,會(huì)不斷地從MessageQueue中取出消息,并將消息Messsge分發(fā)給對(duì)應(yīng)的Handler進(jìn)行處理缤剧。
主線程中馅袁,系統(tǒng)已經(jīng)初始化了一個(gè)Looper對(duì)象,因此可以直接創(chuàng)建Handler即可荒辕,就可以通過Handler來發(fā)送消息汗销、處理消息。程序自己啟動(dòng)的子線程抵窒,程序必須自己創(chuàng)建一個(gè)Looper對(duì)象弛针,bignqie啟動(dòng)它,調(diào)用Looper.prepare()方法李皇。
* **prapare()方法**
保證每個(gè)線程最多只有一個(gè)Looper對(duì)象削茁。
* **looper()方法**
啟動(dòng)Looper,使用一個(gè)死循環(huán)不斷取出MessageQueue中的消息掉房,并將取出的消息分發(fā)給對(duì)應(yīng)的Handler進(jìn)行處理茧跋。
* **MessageQueue**
由Looper負(fù)責(zé)管理,它采用先進(jìn)先出的方式來管理Message卓囚。
* **Handler的構(gòu)造方法**
會(huì)首先得到當(dāng)前線程中保存的Looper實(shí)例瘾杭,進(jìn)而與Looper實(shí)例中的MessageQueue相關(guān)聯(lián)。
* **Handler的sendMessage方法**
會(huì)給msg的target賦值為handler自身捍岳,然后加入MessageQueue中富寿。
參考鏈接:
[線程通信基礎(chǔ)流程分析(Handler睬隶、Looper、Message页徐、MessageQueue)](https://github.com/GeniusVJR/LearningNotes/blob/master/Part1/Android/%E7%BA%BF%E7%A8%8B%E9%80%9A%E4%BF%A1%E5%9F%BA%E7%A1%80%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90.md)
**結(jié)束語:越努力苏潜,越幸運(yùn)!加油 ---王令
QQ:2585085940
郵箱:wang91ling@163.com
歡迎大家光臨寒舍变勇。**