欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入淺析Android消息機(jī)制

 更新時(shí)間:2016年04月05日 11:26:22   作者:byhieg  
在Android中,線程內(nèi)部或者線程之間進(jìn)行信息交互時(shí)經(jīng)常會(huì)使用消息,這些基礎(chǔ)的東西如果我們熟悉其內(nèi)部的原理,將會(huì)使我們?nèi)菀?、更好地架?gòu)系統(tǒng),避免一些低級(jí)的錯(cuò)誤,通過(guò)本文給大家介紹android消息機(jī)制,感興趣的朋友一起學(xué)習(xí)吧

在Android中,線程內(nèi)部或者線程之間進(jìn)行信息交互時(shí)經(jīng)常會(huì)使用消息,這些基礎(chǔ)的東西如果我們熟悉其內(nèi)部的原理,將會(huì)使我們?nèi)菀?、更好地架?gòu)系統(tǒng),避免一些低級(jí)的錯(cuò)誤。

每一個(gè)Android應(yīng)用在啟動(dòng)的時(shí)候都會(huì)創(chuàng)建一個(gè)線程,這個(gè)線程被稱為主線程或者UI線程,Android應(yīng)用的所有操作默認(rèn)都會(huì)運(yùn)行在這個(gè)線程中。
但是當(dāng)我們想要進(jìn)行數(shù)據(jù)請(qǐng)求,圖片下載,或者其他耗時(shí)操作時(shí),是不可能在這個(gè)UI線程做的,因?yàn)锳ndroid在3.0以后的版本已經(jīng)禁止了這件事情,直接拋出一個(gè)異常。所以我們需要一個(gè)子線程來(lái)處理那些除UI操作的事情。

但是這個(gè)又會(huì)有一個(gè)問(wèn)題,我們只能在UI線程進(jìn)程UI操作,只能在子線程進(jìn)行耗時(shí)操作,如果我們需要在耗時(shí)操作結(jié)束后在Android界面上顯示一個(gè)View,我們應(yīng)該怎么做?我們是不可能在子線程直接刷新UI的。這時(shí)我們需要用到Android的消息機(jī)制,來(lái)實(shí)現(xiàn)主線程和子線程的通信。簡(jiǎn)單來(lái)說(shuō),就是子線程獲取到數(shù)據(jù)之后,不直接進(jìn)行UI更新,而是把數(shù)據(jù)裝到消息中發(fā)送到主線程,主線程中有一個(gè)循環(huán)輪詢會(huì)立即收到子線程發(fā)過(guò)來(lái)的信息,然后拿到消息數(shù)據(jù)后在主線程更新UI 。說(shuō)起來(lái)比較簡(jiǎn)單,我們來(lái)仔細(xì)的看一下具體是怎么說(shuō)的。

處理消息的手段——Handler, Looper, MessageQueue

Handler

我們先講解一下Handler,Handler顧名思義就是處理者,通常對(duì)他的用法是在UI線程中新建一個(gè)Handler,并覆寫(xiě)他的handleMessage, 然后再耗時(shí)的線程中將消息post給UI線程,例子如下:

class MyHandler extends Handler{
@Override
public void handleMessage(Message msg){
//更新UI
}
}
MyHandler mHandler = new MyHandler();
new Thread(){
public void run(){
mHandler.sendEmptyMessage(123);
};
}.start(); 

這里規(guī)定了Handler必須在主線程創(chuàng)建,因?yàn)橹挥性赨I線程創(chuàng)建才會(huì)讓Handler關(guān)聯(lián)到已有的MessageQueue。而MessageQueue被封裝到Looper中,而Looper又通過(guò)ThreadLocal封裝到一個(gè)線程中,最后相當(dāng)于MessageQueue關(guān)聯(lián)了一個(gè)線程。所以簡(jiǎn)單來(lái)說(shuō)就是Handler將消息投遞到一個(gè)關(guān)聯(lián)了線程的MessageQueue中,然后Handler在從MessageQueue中取出消息,并且處理它。

我們看一下Handler的2個(gè)常用的方法

void handleMessage(Message msg) : 處理消息的方法
final boolean sendMessage(Message msg) : 立即發(fā)送消息

第一個(gè)方法 我們通常在UI線程中執(zhí)行,一般用來(lái)刷新UI,至于如果創(chuàng)建了一個(gè)非靜態(tài)內(nèi)部類產(chǎn)生對(duì)內(nèi)存泄漏,建議參考這篇博客Handler引發(fā)的內(nèi)存泄漏.第二個(gè)方法我們通常在子線程中執(zhí)行,需要一個(gè)Handler的實(shí)例化對(duì)象,通常是由主線程去去傳遞給子線程。并且需要一個(gè)Message對(duì)象,指定他的msg.what作為消息的標(biāo)示,但是如果我們只是用Handler去處理一個(gè)消息的時(shí)候,選擇post方法是個(gè)更好的選擇,例子如下:

private Handler mHandler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
//UI操作
...
}
});
}
}).start(); 

下面我們接著討論下消息的循環(huán)隊(duì)列MessageQueue與包裝他的Looper循環(huán)

Looper和MessageQueue

上面提到了在UI線程中創(chuàng)建并實(shí)例化Handler對(duì)象不需要Looper和MessageQueue,因?yàn)槲覀兊膽?yīng)用在啟動(dòng)的時(shí)候先執(zhí)行了ActivityThreadMain,在這個(gè)方法就是Java語(yǔ)言運(yùn)行的入口public

static void main(String [] args) 在這里面創(chuàng)建了一個(gè)MainLooper,創(chuàng)建的過(guò)程如下:
public static void main(string[] args){
//初始化
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if(sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
//動(dòng)起來(lái)
Looper.loop();
} 

這里面并沒(méi)有MessageQueue的出現(xiàn),我們可以看一看Looper類的源碼,來(lái)了解在初始化的時(shí)候發(fā)生了什么有趣的事情。

public class Looper {
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper內(nèi)的消息隊(duì)列
final MessageQueue mQueue;
// 當(dāng)前線程
Thread mThread;
// 。。。其他屬性
// 每個(gè)Looper對(duì)象中有它的消息隊(duì)列,和它所屬的線程
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
// 我們調(diào)用該方法會(huì)在調(diào)用線程的TLS中創(chuàng)建Looper對(duì)象
public static final void prepare() {
if (sThreadLocal.get() != null) {
// 試圖在有Looper的線程中再次創(chuàng)建Looper將拋出異常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
// 其他方法
} 

我們一行行的看這段代碼,首先是實(shí)例化一個(gè)ThreadLocal對(duì)象,這個(gè)用來(lái)實(shí)現(xiàn)Looper循環(huán)的本地化存儲(chǔ),關(guān)于ThreadLocal可以看這篇文章為什么用ThreadLocal,簡(jiǎn)而言之就是當(dāng)多個(gè)線程同時(shí)訪問(wèn)Looper對(duì)象的時(shí)候,我們不用synchronized同步機(jī)制來(lái)處理他,而是為每個(gè)線程創(chuàng)建一個(gè)自己的Looper副本,A線程改變了A的looper副本,不影響B(tài)線程的Looper,從而比較高效的實(shí)現(xiàn)線程安全。后面幾句依次定義了MessageQueue,并對(duì)Looper進(jìn)行了私有化構(gòu)造,在prepare方法中將Looper對(duì)象設(shè)置給了sThreadLocal 這樣MessageQueue包裝在了Looper對(duì)象中,同時(shí)通過(guò)ThreadLocal使得線程和Looper關(guān)聯(lián)上,從而消息隊(duì)列與線程關(guān)聯(lián)上,并且不同的線程就不能訪問(wèn)對(duì)方的消息隊(duì)列。

如下圖所示:

接著就是Looper.loop 循環(huán)執(zhí)行起來(lái),我們看一下,在loop方法里面執(zhí)行了發(fā)生了什么事情

public static final void loop() {
Looper me = myLooper(); //得到當(dāng)前線程Looper
MessageQueue queue = me.mQueue; //得到當(dāng)前l(fā)ooper的MQ
while (true) {
Message msg = queue.next(); // 取出message
if (msg != null) {
if (msg.target == null) {
return;
}
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}

這是省略版的代碼,我們從這里看出無(wú)限循環(huán)執(zhí)行,首先從消息隊(duì)列中不斷取出消息,然后不斷msg是否為空,msg.target是否為空,不空的話,執(zhí)行dispatchMessage方法,這個(gè)方法是handler的一個(gè)方法,由此我們可以看出msg.target是handler的類型,至此,通過(guò)Looper.prepare和Loop.loop實(shí)現(xiàn)了MessageQueue,Looper,Handler三者之間的關(guān)聯(lián)。而Handler與Looper,和MessageQueue關(guān)聯(lián)則是在Handler的默認(rèn)構(gòu)造器中,通過(guò)Looper.getLooper獲取loop對(duì)象,從而獲取MessageQueue,其源碼如下:

public Handler(){
//直接把關(guān)聯(lián)looper的MQ作為自己的MQ,因此它的消息將發(fā)送到關(guān)聯(lián)looper的MQ上
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = null;
}

然后我們的流程圖可以多些內(nèi)容,如下所示:

我們接下來(lái)看一下dispatchMessage() 方法,在該方法中實(shí)際上只是一個(gè)分發(fā)方法,如果Runable類型的callback為空,則執(zhí)行handlerMessage來(lái)處理消息,該方法為空,需要覆寫(xiě)。如果不為空,則執(zhí)行handleCallback。實(shí)際上,如果我們用handle的post方法,則就執(zhí)行了callback,如果用sendMessage,則就執(zhí)行了handleMessage
這里無(wú)論是post(Runnable callback)還是handlerMessage實(shí)際上都是在調(diào)用一個(gè)方法sendMessageDelayed(Message msg) 只不過(guò)handlerMessage是直接接受一個(gè)參數(shù),而Runable callback實(shí)際上是將這個(gè)Runable對(duì)象賦給了Message對(duì)象的callback成員變量,最后將Message對(duì)象插入消息隊(duì)列里面。最后Looper不斷從MessageQueue中讀取消息,并且調(diào)用Handler的dispatchMessage消息,在根據(jù)callback是否為空,來(lái)采用不同的方法執(zhí)行。Android消息機(jī)制分析到此結(jié)束。

回到最開(kāi)始

我們這次知道了為什么要在主線程中實(shí)例化Handler對(duì)象才能更新UI刷新,因?yàn)橹挥邪l(fā)送到UI線程的消息,才能被UI線程的handler處理,如果我們要在非UI線程中,實(shí)例化Handler,則必須先將線程變成LooperThread,在實(shí)例化。也就是說(shuō)執(zhí)行如下的代碼:

Loop.prepare();
hander = new Handler;

Loop.loop

至于原因相信讀完上面的講解,應(yīng)該知道。
現(xiàn)在我們看一下我們最開(kāi)始的代碼,最后腦補(bǔ)一下Handler的工作流程。

class MyHandler extends Handler{
@Override
public void handleMessage(Message msg){
//更新UI
}
}
MyHandler mHandler = new MyHandler();
new Thread(){
public void run(){
mHandler.sendEmptyMessage(123);
};
}.start(); 

在Handler實(shí)例化成mHandler的時(shí)候,系統(tǒng)通過(guò)Handler默認(rèn)的構(gòu)造函數(shù)完成了Handler與Looper的關(guān)聯(lián),并通過(guò)Looper關(guān)聯(lián)到了MessageQueue。而主線程的Looper則早在系統(tǒng)啟動(dòng)的時(shí)候通過(guò)Loop.prepare就已經(jīng)構(gòu)造完成了,并與UI線程通過(guò)ThreadLocal關(guān)聯(lián)起來(lái),然后在新的線程中執(zhí)行mHandler.sendEmptyMessage,將Message發(fā)送給了MessageQueue,Looper.loop在循環(huán)的時(shí)候,不斷取出message,交給Handler處理,在我們覆寫(xiě)的HandleMessage中,識(shí)別出我們發(fā)送的消息,將消息處理。當(dāng)然這里只是一個(gè)Empty消息,所以在handleMessage中沒(méi)有去執(zhí)行msg.what的判斷。

以上內(nèi)容是小編給大家介紹的Android消息機(jī)制,希望對(duì)大家有所幫助!

相關(guān)文章

最新評(píng)論