Android組件_Handler Looper Message理解
一半哟、Handler機(jī)制概述
Handler機(jī)制是Android中一種消息處理機(jī)制。
主要組成或重要概念:
- Message,線程間通訊的數(shù)據(jù)單元。
- Message Queue,消息隊(duì)列拣挪,用來存放Handler發(fā)布的消息擦酌,按照FIFO執(zhí)行俱诸。
- Handler是Message的主要處理者,負(fù)責(zé)將Message添加到消息隊(duì)列意見對消息隊(duì)列中的Message進(jìn)行處理赊舶。
- Looper循環(huán)器睁搭,循環(huán)去除Message Queue里面的Message,并交付給相應(yīng)的Handler進(jìn)行處理笼平。
- Thread UI thread通常就是main thread园骆,Android啟動(dòng)程序時(shí)會(huì)替它建立一個(gè)Message Queue。
每一個(gè)線程里可以含有一個(gè)Looper對象以及MessageQueue數(shù)據(jù)結(jié)構(gòu)寓调。 - ThreadLocal 他的作用是幫助Handler獲得當(dāng)前線程的Looper(多個(gè)線程可能有多個(gè)Looper)
二锌唾、使用場景
我們常常用Handler來更新UI,但是不是說Handler就是把用來更新UI的,耗時(shí)的I/O操作晌涕,讀取文件滋捶,訪問網(wǎng)絡(luò)等等都是可以在Handler里面操作的。
子線程間通訊余黎,可以在一個(gè)子線程中去創(chuàng)建一個(gè)Handler重窟,然后使用這個(gè)handler實(shí)例在任何其他線程中發(fā)送消息,最終處理消息的代碼都會(huì)在你創(chuàng)建Handler實(shí)例的線程中運(yùn)行惧财。
延時(shí)任務(wù)等
三巡扇、使用方法
3.1. 子線程線程里更新UI
public class MainActivity extends AppCompatActivity {
//主線程中的handler
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//獲得剛才發(fā)送的Message對象,然后在這里進(jìn)行UI操作
Log.e(TAG,"------------> msg.what = " + msg.what);
...
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在子線程中發(fā)送更新消息給主線程的handler去更新UI
new Thread(new Runnable() {
@Override
public void run() {
Message m = mHandler.obtainMessage();
...
mHandler.sendMessage(m);
}
}).start();
}
}
3.2 子線程間通信
final Handler[] h = new Handler[1];
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//創(chuàng)建子線程的looper垮衷,子線程使用handler消息傳遞厅翔,這一步必須要有,因?yàn)槟J(rèn)的子線程是沒有Looper對象的
h[0] = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e(TAG,"------------> msg.what = " + msg.what);
}
};
Looper.loop();//消息池消息循環(huán)處理
}
}, "work1").start();
new Thread(new Runnable() {
@Override
public void run() {
Message msg = h[0].obtainMessage();
//msg.sendToTarget();
h[0].sendEmptyMessage(0);//在work2子線程中使用持有work1子線程looper的handler發(fā)送消息至work1中去做消息處理
}
}, "work2").start();
3.3 HandlerThread使用
{
MyHandlerThread mHandlerThread = new MyHandlerThread("work");
Handler mHandler = new Handler(mHandlerThread.getLooper()){//將該mHandler與HandlerThread對象的Looper關(guān)聯(lián)起來
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
MyHandlerThread mHandlerThread = new MyHandlerThread("work");
Handler mHandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
3.4 Handler CallBack參數(shù)使用
在構(gòu)造Handler對象的時(shí)候可以穿入CallBack參數(shù)搀突,如下
//主線程:
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
//其他線程中:
handler.sendMessageXXX(msg);
3.5 Handler post方法使用
//主線程中創(chuàng)建mPostHandler
Handler mPostHandler = new Handler();
//子線程中使用post方法
new Thread(new Runnable() {
@Override
public void run() {
/* Message m = mHandler.obtainMessage();
mHandler.sendMessage(m);*/
mPostHandler.post(new Runnable() {
@Override
public void run() {
//更新UI 知给,注意這里雖然操作寫在子線程中,事實(shí)運(yùn)行時(shí)是在主線程中描姚,原因分析在4.2.5中
}
});
}
}).start();
3.6 Handler 延時(shí)任務(wù)/循環(huán)定時(shí)操作
延時(shí)任務(wù)
new Handler().postDelayed(new Runnable(){
public void run() {
//show dialog
}
}, 5000); //延時(shí)5秒后執(zhí)行runnable run方法
循環(huán)定時(shí)操作:
1涩赢,首先創(chuàng)建一個(gè)Handler對象
Handler handler=new Handler();
2,然后創(chuàng)建一個(gè)Runnable對
Runnable runnable=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
//要做的事情轩勘,這里再次調(diào)用此Runnable對象筒扒,以實(shí)現(xiàn)每兩秒實(shí)現(xiàn)一次的定時(shí)器操作
handler.postDelayed(this, 2000);
}
};
3,使用PostDelayed方法绊寻,兩秒后調(diào)用此Runnable對象
handler.postDelayed(runnable, 2000);
4花墩,如果想要關(guān)閉此定時(shí)器,可以這樣操作
handler.removeCallbacks(runnable);
四澄步、原理分析
Handler機(jī)制的鐵三角-Handler冰蘑、Looper和MessageQueue,另外還有下面進(jìn)行淺入分析村缸。
整體架構(gòu) :
4.1 主線程Handler對象與主線程Looper對象關(guān)聯(lián)
對3.1中的例子進(jìn)行淺入的分析祠肥。
4.1.1 Handler對象初始化獲取主線程的Looper對象
1.主線程Handler對象初始化:
Handler mHandler = new Handler(){...}
默認(rèn)的構(gòu)造函數(shù)會(huì)將該Handler對象與當(dāng)前線程的Looper對象關(guān)聯(lián)。下面我們看下構(gòu)造函數(shù)是如何關(guān)聯(lián)上當(dāng)前線程的looper的梯皿。
Handler的默認(rèn)構(gòu)造函數(shù):
frameworks/base/core/java/android/os/Handler.java
public Handler() {
this(callback:null, async:false);
}
2.如果該線程沒有l(wèi)ooper對象仇箱,該handler將拋異常:
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();//
if (mLooper == null) {//如果當(dāng)前線程沒有Looper對象將拋出異常
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
3.sThreadLocal對象中保存著當(dāng)前線程的looper對象,Looper.myLooper()即用來獲取當(dāng)前線程的Looper對象:
frameworks/base/core/java/android/os/Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//這里獲取當(dāng)前線程的Looper對象
}
4.1.2 主線程初始化時(shí)創(chuàng)建Looper對象及MessageQueue對象
1.對于UI線程即我們所說的Main線程及ActivityThread,在ActivityThread創(chuàng)建的時(shí)候东羹,main函數(shù)中創(chuàng)建了Looper對象剂桥,并通過Looper.loop()是主線程進(jìn)入消息循環(huán)中:
frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
...
Looper.prepareMainLooper(); //創(chuàng)建主線程Looper對象
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();//不斷循環(huán)處理MessageQueue中的消息
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2.Looper.prepareMainLooper()創(chuàng)建主線程的Looper以及MessageQueue,并通過Looper.loop()來開啟主線程的消息循環(huán):
frameworks/base/core/java/android/os/Looper.java
public static void prepareMainLooper() {
prepare(false);//創(chuàng)建Looper對象属提,并且將新建的looper對象與當(dāng)前線程關(guān)聯(lián)
synchronized (Looper.class) {
if (sMainLooper != null) { //可以看出主線程應(yīng)該只有一個(gè)Looper對象
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();//返回當(dāng)前線程的Looper對象
}
}
3.Looper.prepare()函數(shù)為當(dāng)前線程(此處為主線程权逗,也可以是其他線程)創(chuàng)建Looper對象,并將該對象設(shè)置至線程的sThreadLocal變量中:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { // 說明prepare不能調(diào)用兩次,否則會(huì)拋出異常斟薇,保證一個(gè)線程只有一個(gè)Looper實(shí)例
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//sThreadLocal是一個(gè)ThreadLocal對象火惊,可以在一個(gè)線程中存儲(chǔ)變量。
}
4.Looper.myLooper()返回當(dāng)前線程的Looper對象奔垦,這里也就驗(yàn)證了myLooper()的確是從sThreadLocal變量中獲取了當(dāng)前線程的Looper對象屹耐。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
概述一下就是,主線程在初始化時(shí)就生成了含有一個(gè)不可退出的MessageQueue的Looper對象椿猎,并將該Looper對象保存在主線程的sThreadLocal變量中惶岭;
當(dāng)在主線程中創(chuàng)建Handler對象時(shí),會(huì)將該Handler對象與當(dāng)前線程的sThreadLocal變量中保存的主線程Looper對象關(guān)聯(lián)起來犯眠。
因此按灶,在上面3.1的例子中,主線程new一個(gè)Handler對象時(shí)會(huì)將該Handler對象與主線程中的Looper對象關(guān)聯(lián)筐咧,執(zhí)行Looper.loop()不斷循環(huán)處理消息鸯旁;
4.2 Handler、Looper量蕊、Message及HandlerThread淺析
4.2.1 Handler
1.可以使用的方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageDelayed(Message,long)
sendMessageAtTime(Message,long)
2.以上方法都最終都將調(diào)用sendMessageAtTime(),將消息加入msg的目標(biāo)Handler對象關(guān)聯(lián)的Looper持有的消息隊(duì)列中铺罢,之后就是排隊(duì)消息隊(duì)列循環(huán)處理消息了:
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); //將該消息加入該消息的目標(biāo)handler關(guān)聯(lián)的looper中的消息隊(duì)列中
}
4.2.2 Looper:
1.Looper構(gòu)造函數(shù),看到Looper的構(gòu)造函數(shù)里新建了一個(gè)MessageQueue對象:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//創(chuàng)建消息隊(duì)列
mThread = Thread.currentThread();
}
2.Looper.loop(),循環(huán)處理消息隊(duì)列中消息的函數(shù):
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
...
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block残炮,若無消息則會(huì)阻塞在這里韭赘。
...
try {
msg.target.dispatchMessage(msg); //調(diào)用發(fā)該message的handler的dispatchMessage方法;
} finally {
}
...
msg.recycleUnchecked();//處理完的msg可以進(jìn)行回收势就,實(shí)現(xiàn)的地方清除了該msg對象的內(nèi)容泉瞻,并將其保留在消息池中,以供循環(huán)使用
}
}
3.在dispatchMessage中進(jìn)行消息處理苞冯,post方法中的runnable就是這里的msg.callback
frameworks/base/core/java/android/os/Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); //執(zhí)行post方法中的runnable方法
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { //如果創(chuàng)建handler時(shí)有傳入callback的話
return;
}
}
handleMessage(msg);//創(chuàng)建handler時(shí)需復(fù)寫方法handleMessage(),處理消息
}
}
3.1首先會(huì)判斷msg.callback存不存在袖牙,msg.callback是Runnable類型,如果msg.callback存在舅锄,那么說明該Message是通過執(zhí)行Handler的postXXX系列方法將Message放入到消息隊(duì)列中的鞭达,這種情況下會(huì)執(zhí)行handleCallback(msg), handleCallback源碼如下:
private static void handleCallback(Message message) {
message.callback.run();
}
這樣我們我們就清楚地看到我們執(zhí)行了msg.callback的run方法,也就是執(zhí)行了postXXX所傳遞的Runnable對象的run方法巧娱。
3.2.如果我們不是通過postXXX系列方法將Message放入到消息隊(duì)列中的碉怔,那么msg.callback就是null,代碼繼續(xù)往下執(zhí)行禁添,接著我們會(huì)判斷Handler的成員字段mCallback存不存在。mCallback是Hanlder.Callback類型的桨踪,我們在上面提到過老翘,在Handler的構(gòu)造函數(shù)中我們可以傳遞Hanlder.Callback類型的對象,該對象需要實(shí)現(xiàn)handleMessage方法,如果我們在構(gòu)造函數(shù)中傳遞了該Callback對象铺峭,那么我們就會(huì)讓Callback的handleMessage方法來處理Message墓怀。
3.3.如果我們在構(gòu)造函數(shù)中沒有傳入Callback類型的對象,那么mCallback就為null,那么我們會(huì)調(diào)用Handler自身的hanldeMessage方法卫键,該方法默認(rèn)是個(gè)空方法傀履,我們需要自己是重寫實(shí)現(xiàn)該方法。
綜上莉炉,我們可以看到Handler提供了三種途徑處理Message钓账,而且處理有前后優(yōu)先級(jí)之分:首先嘗試讓postXXX中傳遞的Runnable執(zhí)行,其次嘗試讓Handler構(gòu)造函數(shù)中傳入的Callback的handleMessage方法處理絮宁,最后才是讓Handler自身的handleMessage方法處理Message梆暮。
4.2.3 MessageQueue
1 MessageQueue的next()方法
在消息隊(duì)列中不斷取出消息,next方法是個(gè)無限循環(huán)的方法绍昂,如果有消息返回這條消息并從鏈表中移除啦粹,而沒有消息則一直阻塞在這里。
Message next() {
for (;;) {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
...
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
}
}
}
4.2.4 HandlerThread
1.自帶Looper的Thread:
frameworks/base/core/java/android/os/HandlerThread.java
public class HandlerThread extends Thread {
@Override
public void run() {
...
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
onLooperPrepared();
Looper.loop();
...
}
}
我們看到HandlerThread繼承自Thread窘游,并且在run函數(shù)中使用Looper.prepare()為使用線程創(chuàng)建了一個(gè)Looper對象唠椭,并且把該對象放到了該線程范圍內(nèi)的變量中(sThreadLocal);在Looper對象的構(gòu)造過程中忍饰,初始化了一個(gè)MessageQueue泪蔫,作為該Looper對象成員變量;loop()開啟了喘批,不斷的循環(huán)從MessageQueue中取消息處理了撩荣,當(dāng)沒有消息的時(shí)候會(huì)阻塞,有消息的到來的時(shí)候會(huì)喚醒饶深。
因此也可以使用HandlerThread餐曹,這樣可以不用像普通線程那樣需要Looper.prepare()和Looper.loop(),因?yàn)镠andlerThread為我們做好了準(zhǔn)備工作敌厘;
2.另外可以通過HandlerThread的getLooper方法獲的該HandlerThread的Looper對象:
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
handleMessage
4.2.5 Handler中post方法
3.5中的例子中子線程中post中的run方法運(yùn)行在主線程中台猴,原因分析如下:
1.傳入Runnable對象
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
2.這里將Runnable封裝到一個(gè)Message對象中
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r; //將runnable傳入message對象中的callback變量中
return m;
}
3.之后與普通的handler sendMessage方法流程一致,調(diào)用用sendMessageAtTime俱两,將該消息加入該消息的目標(biāo)handler饱狂,及mPostHandler關(guān)聯(lián)的looper中的消息隊(duì)列中,如何處理在4.2.1 中有提到過宪彩。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
也就是說休讳,雖然runnable復(fù)寫方法在子線程中,但是同樣是封裝成message對象傳入主線程的looper持有的MessageQueue對象中尿孔,在主線程中對封裝了runnable變量的message進(jìn)行消息執(zhí)行處理俊柔,因此post中的run方法是運(yùn)行在主線程中的筹麸。
五、注意事項(xiàng)
- Handler在處理消息需要嚴(yán)格區(qū)分是否是在UI線程中雏婶,Handler一般用在非UI線程中來傳遞消息物赶,在非UI線程中使用Handler來發(fā)送消息,消息
處理會(huì)被嚴(yán)格執(zhí)行留晚,但如果在UI線程中使用Handler來發(fā)送消息酵紫,相同的消息在內(nèi)部會(huì)被合并,且執(zhí)行時(shí)序也得不到保證 - 因?yàn)镠andlerThread擁有自己的消息隊(duì)列错维,它不會(huì)干擾或阻塞UI線程奖地,比較合適處理那些需要花費(fèi)時(shí)間偏長的任務(wù)。我們只需要把任務(wù)發(fā)送給HandlerThread需五,然后就只需要等待任務(wù)執(zhí)行結(jié)束的時(shí)候通知返回到主線程就好了鹉动。
- android中handler使用應(yīng)該注意的由handler引起的OOM內(nèi)存泄漏) http://blog.csdn.net/javazejian/article/details/50839443
- 在普通線程中使用機(jī)制時(shí)要記得先Looper.prepare(); 最后還要Looper.loop();