1. ANR異常
Application No Response:應(yīng)用程序無響應(yīng)。在主線程中,是不允許執(zhí)行耗時的操作的,如果主線程阻塞的時間大于6秒,就很有可能出現(xiàn)anr異常。主線程,要完成界面的更新,事件的處理,窗體顯示的回調(diào),所以如果主線程阻塞時間較長,就不能很好的處理以上比較重要的事情,那么Android有一個機制,就是如果他發(fā)現(xiàn)消息隊列中有很多消息,主線程沒辦法響應(yīng)的話,他就會拋出anr異常。所以,比較耗時的操作都必須要交給子線程。
解決辦法:可以通過Handler來解決這個問題,將比較耗時的操作交給子線程,然后子線程通過Handler,發(fā)送消息給主線程,讓主線程去更新界面。什么樣的操作時比較耗時的?
1、訪問網(wǎng)絡(luò),2、大文件的拷貝,3、阻塞式的請求,socket
2. Handler、Looper、Message、MessageQueue
Android 的Handler 機制(也有人叫消息機制)目的是為了跨線程通信,也就是多線程通信。之所以需要跨線程通信是因為在Android 中主線程通常只負(fù)責(zé)UI 的創(chuàng)建和修改,子線程負(fù)責(zé)網(wǎng)絡(luò)訪問和耗時操作,因此,主線程和子線程需要經(jīng)常配合使用才能完成整個Android 功能。
在Android中,線程內(nèi)部或者線程之間進(jìn)行信息交互時經(jīng)常會使用消息,這些基礎(chǔ)的東西如果我們熟悉其內(nèi)部的原理,將會使我們?nèi)菀?、更好地架?gòu)系統(tǒng),避免一些低級的錯誤。在學(xué)習(xí)Android中消息機制之前,我們先了解與消息有關(guān)的幾個類:
- Handler:消息處理器,發(fā)送消息和處理消息。你可以構(gòu)造Handler對象來與Looper溝通,以便push新消息到Message Queue里,或者接收Looper(從Message Queue取出)所送來的消息。
- Looper:輪詢器,從messagequeue取消息,分發(fā)給handler處理。一個線程可以產(chǎn)生一個Looper對象,由它來管理此線程里的Message Queue(消息隊列)
- Message 消息,數(shù)據(jù)的載體
- MessageQueue 消息隊列,存儲消息
當(dāng)我們的Android應(yīng)用程序的進(jìn)程一創(chuàng)建的時候,系統(tǒng)就給這個進(jìn)程提供了一個Looper,Looper是一個死循環(huán),它內(nèi)部維護(hù)這個一個消息隊列。Looper不停地從消息隊列中取消息(Message),取到消息就發(fā)送給了Handler,最后Handler根據(jù)接收到的消息去修改UI。Handler的sendMessage方法就是將消息添加到消息隊列中。
3. UI線程
線程:UI thread 通常就是main thread,而Android啟動程序時會替它建立一個Message Queue。
每一個線程里可含有一個Looper對象以及一個MessageQueue數(shù)據(jù)結(jié)構(gòu)。在你的應(yīng)用程序里,可以定義Handler的子類別來接收Looper所送出的消息。在你的Android程序里,新誕生一個線程,或執(zhí)行 (Thread)時并不會自動建立其Message Looper。
Android里并沒有Global的Message Queue數(shù)據(jù)結(jié)構(gòu),例如,不同APK里的對象不能透過Massage Queue來交換訊息(Message)。
例如:線程A的Handler對象可以傳遞消息給別的線程,讓別的線程B或C等能送消息來給線程A(存于A的Message Queue里)。線程A的Message Queue里的消息,只有線程A所屬的對象可以處理。使用Looper.myLooper()可以取得當(dāng)前線程的Looper對象。可以自定義Handler類,只要繼承Handler即可。使用new EventHandler(Looper.myLooper()); 可用來構(gòu)造當(dāng)前線程的Handler對象(其中EventHandler是自定義的Handler類)。
4. Activity.runOnUiThread()
Activity中提供了一個runOnUiThread方法,用于進(jìn)行消息處理。此方法是通過線程合并join來實現(xiàn)消息處理的。
線程合并:主線程將子線程的任務(wù)拿到自己這里來執(zhí)行并終止子線程。實例代碼如下:
/**
* Runs the specified action on the UI thread. If thecurrent thread is
* the UI thread, then the action is executedimmediately. If the
* current thread is not the UI thread, the action is posted to the
* event queue of the UI thread.
*
* 上面的意思為:在UI線程中運行我們的任務(wù),如果當(dāng)前線程是UI線程,則立即執(zhí)行,如果
* 不是則該任務(wù)發(fā)送到UI線程的事件隊列。
*/
runOnUiThread(new Runnable() {
@Override
public void run() {
//自定義我們的業(yè)務(wù)代碼
}
});
5. View.post()、View.postDelayed()
6. Message消息
消息對象,顧名思義就是記錄消息信息的類。這個類有幾個比較重要的字段:
- arg1和arg2:我們可以使用兩個字段用來存放我們需要傳遞的整型值,在Service中,我們可以用來存放Service的ID。
- obj:該字段是Object類型,我們可以讓該字段傳遞某個多項到消息的接受者中。
- what:這個字段可以說是消息的標(biāo)志,在消息處理中,我們可以根據(jù)這個字段的不同的值進(jìn)行不同的處理,類似于我們在處理Button事件時,通過switch(v.getId())判斷是點擊了哪個按鈕。
在使用Message時,我們可以通過new Message()創(chuàng)建一個Message實例,但是Android更推薦我們通過Message.obtain()或者Handler.obtainMessage()獲取Message對象。這并不一定是直接創(chuàng)建一個新的實例,而是先從消息池中看有沒有可用的Message實例,存在則直接取出并返回這個實例。反之如果消息池中沒有可用的Message實例,則根據(jù)給定的參數(shù)new一個新Message對象。通過分析源碼可得知,Android系統(tǒng)默認(rèn)情況下在消息池中實例化10個Message對象。
//創(chuàng)建或獲取消息的幾種方式
Message msg = new Message();// 創(chuàng)建一個新的消息對象
Message msg = handler.obtainMessage();// 獲取一個消息,如果消息池存在消息,則復(fù)用消息池中的消息,否則新創(chuàng)建一個消息對象
Message msg = Message.obtain();
Message.obtain(handler, what, obj).sendToTarget();
7. MessageQueue消息隊列
消息隊列,用來存放Message對象的數(shù)據(jù)結(jié)構(gòu),按照“先進(jìn)先出”的原則存放消息。存放并非實際意義的保存,而是將Message對象以鏈表的方式串聯(lián)起來的。MessageQueue對象不需要我們自己創(chuàng)建,而是有Looper對象對其進(jìn)行管理,一個線程最多只可以擁有一個MessageQueue。我們可以通過Looper.myQueue()獲取當(dāng)前線程中的MessageQueue。
MessageQueue的管理者,在一個線程中,如果存在Looper對象,則必定存在MessageQueue對象,并且只存在一個Looper對象和一個MessageQueue對象。
public class Looper {
MessageQueue mQueue;//Looper身上維持著一個消息隊列
...
}
在Android系統(tǒng)中,除了主線程有默認(rèn)的Looper對象,其它線程默認(rèn)是沒有Looper對象。如果想讓我們新創(chuàng)建的線程擁有Looper對象時,我們首先應(yīng)調(diào)用Looper.prepare()方法,然后再調(diào)用Looper.loop()方法。典型的用法如下:
class LooperThread extends Thread
{
public Handler mHandler;
public void run()
{
Looper.prepare();
//其它需要處理的操作
Looper.loop();
}
}
倘若我們的線程中存在Looper對象,則我們可以通過Looper.myLooper()獲取,此外我們還可以通過Looper.getMainLooper()獲取當(dāng)前應(yīng)用系統(tǒng)中主線程的Looper對象。在這個地方有一點需要注意,假如Looper對象位于應(yīng)用程序主線程中,則Looper.myLooper()和Looper.getMainLooper()獲取的是同一個對象。
8. Handler消息處理器
消息的處理者。通過Handler對象我們可以封裝Message對象,然后通過sendMessage(msg)把Message對象添加到MessageQueue中;當(dāng)MessageQueue循環(huán)到該Message時,就會調(diào)用該Message對象對應(yīng)的handler對象的handleMessage()方法對其進(jìn)行處理。由于是在handleMessage()方法中處理消息,因此我們應(yīng)該編寫一個類繼承自Handler,然后在handleMessage()處理我們需要的操作。
下面我們通過跟蹤代碼分析在Android中是如何處理消息。首先貼上測試代碼:
public class MessageService extends Service
{
private static final String TAG = "MessageService";
private static final int KUKA = 0;
private Looper looper;
private ServiceHandler handler;
/**
* 由于處理消息是在Handler的handleMessage()方法中,因此我們需要自己編寫類
* 繼承自Handler類,然后在handleMessage()中編寫我們所需要的功能代碼
* @author coolszy
*/
private final class ServiceHandler extends Handler
{
public ServiceHandler(Looper looper)
{
super(looper);
}
@Override
public void handleMessage(Message msg)
{
// 根據(jù)what字段判斷是哪個消息
switch (msg.what)
{
case KUKA:
//獲取msg的obj字段。我們可在此編寫我們所需要的功能代碼
Log.i(TAG, "The obj field of msg:" + msg.obj);
break;
// other cases
default:
break;
}
// 如果我們Service已完成任務(wù),則停止Service
stopSelf(msg.arg1);
}
}
@Override
public void onCreate()
{
Log.i(TAG, "MessageService-->onCreate()");
// 默認(rèn)情況下Service是運行在主線程中,而服務(wù)一般又十分耗費時間,如果
// 放在主線程中,將會影響程序與用戶的交互,因此把Service
// 放在一個單獨的線程中執(zhí)行
HandlerThread thread = new HandlerThread("MessageDemoThread", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// 獲取當(dāng)前線程中的looper對象
looper = thread.getLooper();
//創(chuàng)建Handler對象,把looper傳遞過來使得handler、
//looper和messageQueue三者建立聯(lián)系
handler = new ServiceHandler(looper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.i(TAG, "MessageService-->onStartCommand()");
//從消息池中獲取一個Message實例
Message msg = handler.obtainMessage();
// arg1保存線程的ID,在handleMessage()方法中
// 我們可以通過stopSelf(startId)方法,停止服務(wù)
msg.arg1 = startId;
// msg的標(biāo)志
msg.what = KUKA;
// 在這里我創(chuàng)建一個date對象,賦值給obj字段
// 在實際中我們可以通過obj傳遞我們需要處理的對象
Date date = new Date();
msg.obj = date;
// 把msg添加到MessageQueue中
handler.sendMessage(msg);
return START_STICKY;
}
@Override
public void onDestroy()
{
Log.i(TAG, "MessageService-->onDestroy()");
}
@Override
public IBinder onBind(Intent intent)
{
return null;
}
}
運行結(jié)果:
注:在測試代碼中我們使用了HandlerThread類,該類是Thread的子類,該類運行時將會創(chuàng)建looper對象,使用該類省去了我們自己編寫Thread子類并且創(chuàng)建Looper的麻煩。下面我們分析下程序的運行過程:
8.1 onCreate()
首先啟動服務(wù)時將會調(diào)用onCreate()方法,在該方法中我們new了一個HandlerThread對象,提供了線程的名字和優(yōu)先級。緊接著我們調(diào)用了start()方法,執(zhí)行該方法將會調(diào)用HandlerThread對象的run()方法:
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在run()方法中,系統(tǒng)給線程添加的Looper,同時調(diào)用了Looper的loop()方法:
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
通過源碼我們可以看到loop()方法是個死循環(huán),將會不停的從MessageQueue對象中獲取Message對象,如果MessageQueue 對象中不存在Message對象,則結(jié)束本次循環(huán),然后繼續(xù)循環(huán);如果存在Message對象,則執(zhí)行 msg.target.dispatchMessage(msg),但是這個msg的.target字段的值是什么呢?我們先暫時停止跟蹤源碼,返回到onCreate()方法中。線程執(zhí)行完start()方法后,我們可以獲取線程的Looper對象,然后new一個ServiceHandler對象,我們把Looper對象傳到ServiceHandler構(gòu)造函數(shù)中將使handler、looper和messageQueue三者建立聯(lián)系。
8.2 onStartCommand()
執(zhí)行完onStart()方法后,將執(zhí)行onStartCommand()方法。首先我們從消息池中獲取一個Message實例,然后給Message對象的arg1、what、obj三個字段賦值。緊接著調(diào)用sendMessage(msg)方法,我們跟蹤源代碼,該方法將會調(diào)用sendMessageDelayed(msg, 0)方法,而sendMessageDelayed()方法又會調(diào)用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在該方法中我們要注意該句代碼msg.target = this,msg的target指向了this,而this就是ServiceHandler對象,因此msg的target字段指向了ServiceHandler對象,同時該方法又調(diào)用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法:
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != 0) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
this.notify();
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
return true;
}
該方法主要的任務(wù)就是把Message對象的添加到MessageQueue中(數(shù)據(jù)結(jié)構(gòu)最基礎(chǔ)的東西,自己畫圖理解下)。
handler.sendMessage()-->handler.sendMessageDelayed()-->handler.sendMessageAtTime()-->msg.target = this;queue.enqueueMessage==>把msg添加到消息隊列中
8.3 handleMessage(msg)
onStartCommand()執(zhí)行完畢后我們的Service中的方法就執(zhí)行完畢了,那么handleMessage()是怎么調(diào)用的呢?在前面分析的loop()方法中,我們當(dāng)時不知道m(xù)sg的target字段代碼什么,通過上面分析現(xiàn)在我們知道它代表ServiceHandler對象,msg.target.dispatchMessage(msg);則表示執(zhí)行ServiceHandler對象中的dispatchMessage()方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
該方法首先判斷callback是否為空,我們跟蹤的過程中未見給其賦值,因此callback字段為空,所以最終將會執(zhí)行handleMessage()方法,也就是我們ServiceHandler類中復(fù)寫的方法。在該方法將根據(jù)what字段的值判斷執(zhí)行哪段代碼。
至此,我們看到,一個Message經(jīng)由Handler的發(fā)送,MessageQueue的入隊,Looper的抽取,又再一次地回到Handler的懷抱中。而繞的這一圈,也正好幫助我們將同步操作變成了異步操作。
9. Handler的源碼分析
先看構(gòu)造方法
public class Handler {
private Looper mLooper;
private MessageQueue mQueue;
public Handler() {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
}
在Handler的構(gòu)造方法中,調(diào)用Looper.myLooper()方法獲取一個Looper對象,如果Looper對象為空,則會拋異常,沒有Looper對象不能創(chuàng)建Handler對象。但是我們在主線程new Handler的時候,并沒有調(diào)用Looper.prepare()和Looper.loop()方法初始化Looper,也不會出現(xiàn)異常,這是因為Android系統(tǒng)在主線程創(chuàng)建的時候幫我們把Looper初始化了
9.1 主線程設(shè)置Looper,在ActivityThread類里面
public static final void main(String[] args) {
....
// 1.主線程創(chuàng)建Looper
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
}
MainThread 是Android 系統(tǒng)創(chuàng)建并維護(hù)的,創(chuàng)建的時候系統(tǒng)執(zhí)行了Looper.prepare();方法,該方法內(nèi)部創(chuàng)建了MessageQueue 消息隊列(也叫消息池),該消息隊列是Message 消息的容器,用于存儲通過handler發(fā)送過來的Message。MessageQueue 是Looper 對象的成員變量,Looper 對象通過ThreadLocal 綁定在MainThread 中。因此我們可以簡單的這么認(rèn)為:MainThread 擁有唯一的一個Looper 對象,該Looper 對象有用唯一的MessageQueue 對象,MessageQueue 對象可以存儲多個Message。
MainThread 中需要程序員手動創(chuàng)建Handler 對象,并覆寫Handler 中的handleMessage(Message msg)方法,該方法將來會在主線程中被調(diào)用,在該方法里一般會寫與UI 修改相關(guān)的代碼。
MainThread 創(chuàng)建好之后,系統(tǒng)自動執(zhí)行了Looper.loop();方法,該方法內(nèi)部開啟了一個“死循環(huán)”不斷的去之前創(chuàng)建好的MessageQueue 中取Message。如果一有消息進(jìn)入MessageQueue,那么馬上會被Looper.loop();取出來,取出來之后就會調(diào)用之前創(chuàng)建好的handler 對象的handleMessage(Message)方法。
newThread 線程是我們程序員自定new 出來的子線程。在該子線程中處理完我們的“耗時”或者網(wǎng)絡(luò)訪問任務(wù)后,調(diào)用主線程中的handler 對象的sendMessage(msg)方法,該方法一被執(zhí)行,內(nèi)部將就msg添加到了主線程中的MessageQueue 隊列中,這樣就成為了Looper.loop()的盤中餐了,等待著被消費。
上面的過程有點類似生產(chǎn)者和消費者的過程。newThread 屬于生產(chǎn)者,負(fù)責(zé)生產(chǎn)Message,MainThread 屬于消費者。這是一個很復(fù)雜的過程,但是Android 顯然已經(jīng)將這種模式給封裝起來了,就叫Handler 機制。我們使用時只需要在主線程中創(chuàng)建Handler,并覆寫handler 中的handleMessage 方法,然后在子線程中調(diào)用handler 的sendMessage(msg)方法即可。
獲取Looper對象后,接著獲取Looper身上的MessageQueue對象,Handler就是把消息發(fā)送到該消息隊列
Handler對象創(chuàng)建后,就可以通過handler.sendXxx()發(fā)送消息,可以發(fā)送一個普通的消息,也可以發(fā)送一個空消息,可以發(fā)送一個延時消息,也可以發(fā)送一個定時消息
public class Handler {
...
// 發(fā)送一個普通消息
public boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0)
}
// 發(fā)送一個空消息
public boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
// 發(fā)送一個空的延時消息
public boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, 0);
}
// 發(fā)送一個延時消息
public boolean sendMessageDelayed(Message msg, long delayTime) {
if (delayTime < 0)
delayTime = 0;
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayTime);
}
// 發(fā)送一個定時消息
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
//發(fā)送消息幾個方法sendXxx(),最終都是調(diào)用enqueueMessage()方法,消息入隊
public boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 把Message的target置為當(dāng)前發(fā)送的Handler,以便Looper取到message后根據(jù)target把message分發(fā)給正確的Handler
msg.target = this;
// 往隊列里面添加消息Message
return queue.enqueueMessage(msg, uptimeMillis);
}
...
}
發(fā)送消息幾個方法sendXxx(),最終都是調(diào)用enqueueMessage()方法,在該方法內(nèi)部調(diào)用的是消息隊列MessageQueue的enqueueMessage()方法,把消息發(fā)送到消息隊列
把this,也就是當(dāng)前handler對象賦值給Message 的target屬性,當(dāng)多個Handler發(fā)送消息到消息隊列的時候,可以通過該屬性判斷消息是哪個Handler發(fā)送的
9.2 MessageQueue.enqueueMessage
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 當(dāng)前發(fā)送的message需要馬上被處理調(diào),needWake喚醒狀態(tài)置true
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 當(dāng)前發(fā)送的message被排隊到其他message的后面,needWake喚醒狀態(tài)置false
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (; ; ) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) { // 是否喚醒主線程
nativeWake(mPtr);
}
}
return true;
}
9.3 Handler的post()、postAtTime()、postDelayed()
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
// 把Runnable包裝成一個消息Message
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();// 獲取消息對象
m.callback = r;// 把消息賦值給Message的callback屬性
return m;
}
可以調(diào)用Handler的post()、postAtTime()、postDelayed()分別發(fā)送一個普通的、定時、延時的Runnable任務(wù),Runnable會賦值給Message的callback屬性,最終封裝成一個消息發(fā)送出去
9.4 刪除Callback和Message
- Handler.removeCallbacks() 從消息隊列中刪除所有回調(diào)
- Handler.removeMessages() 從消息隊列刪除所有消息
- Handler.removeCallbacksAndMessages() 從消息隊列中刪除所有Message和Callback
一般在Activity銷毀的時候調(diào)用
public void onDestroy(){
handler.removeCallbacks();
handler.removeMessages();
handler.removeCallbacksAndMessages();
}
**10. Looper **
輪詢器,從messagequeue取消息,分發(fā)給handler處理。創(chuàng)建Handler對象,必須有Looper對象,而Looper對象的初始化需要調(diào)用Looper.prepare()和Looper.loop()方法
10.1 Looper.prepare()
ThreadLocal<Looper> sThreadLocal;
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 3、在主線程中設(shè)置Looper, new Looper()里面創(chuàng)建了一個MessageQueue
sThreadLocal.set(new Looper());
}
public static final void prepareMainLooper() {
// 2、調(diào)用prepare
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
先從ThreadLocal中獲取一個Looper對象,如果該Looper對象不為空,則拋異常,這是因為一個線程僅能夠綁定一個Looper對象。ThreadLocal是一個用于線程范圍內(nèi)共享數(shù)據(jù)的底層是一個map結(jié)構(gòu)的類,key是當(dāng)前線程,value是Looper。如果當(dāng)前線程沒有綁定Looper對象,則new Looper()創(chuàng)建一個Looper對象,并把該Looper對象設(shè)置sThreadLocal
10.2 Looper.loop()
public static void loop() {
Looper me = myLooper(); // 獲取當(dāng)前線程的Looper對象,為空則拋異常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue; // 獲取Looper對象維持的消息隊列
for (; ; ) { // 開啟死循環(huán)從消息隊列獲取消息
// 調(diào)用MessageQueue的next()取消息,如果沒有消息,就阻塞
Message msg = queue.next();
// msg.target即Handler,獲取消息后調(diào)用Handler的dispatchMessage()處理消息
msg.target.dispatchMessage(msg);
}
}
主線程調(diào)用Looper.loop()方法,主線程就會阻塞,是一個死循環(huán),使用管道(Pipe),是Linux中的一種進(jìn)程間通信方式,使用了特殊的文件,有兩個文件描述符(一個是讀取,一個是寫入)
應(yīng)用場景;主進(jìn)程拿著讀取描述符等待讀取,沒有內(nèi)容時就阻塞,另一個進(jìn)程拿寫入描述符去寫內(nèi)容,喚醒主進(jìn)程,主進(jìn)程拿著讀取描述符讀取到內(nèi)容,繼續(xù)執(zhí)行。
Handler應(yīng)用場景:Handler在主線程中創(chuàng)建,Looper會在死循環(huán)里等待取消息,1、沒取到,就阻塞,2、一旦被子線程喚醒,取到消息,就把Message交給Handler處理。子線程用Handler去發(fā)送消息,拿寫入描述符去寫消息,喚醒主線程。
11. Handler.dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 把Message交給Handler處理
handleMessage(msg);
}
}
調(diào)用Looper.loop()會開啟一個死循環(huán),從消息隊列MessageQueue取消息,取到消息后調(diào)用msg.target.dispatchMessage(msg);即調(diào)用Handler的dispatchMessage()方法,在該方法內(nèi)部調(diào)用的是handleMessage(),對,就是我們new Handler的時候?qū)崿F(xiàn)的handleMessage()方法
所以Android的消息機制大概流程是:Handler把消息Message發(fā)送到消息隊列MessageQueue,Looper從消息隊列取消息,取到消息后回調(diào)Handler的handleMessage()方法
11.1 消息處理的優(yōu)先級
在dispatchMessage()方法中,如果msg.callback(一個Runnable)不為空,則先處理Message的Runnable;然后判斷mCallback(通過Handler的構(gòu)造方法傳進(jìn)來的Callback)是否為空,不為空,則執(zhí)行Callback的handleMessage()方法,最后才是執(zhí)行Handler的handleMessage()
所有消息處理的優(yōu)先級是Message的callback --> Handler的mCallback --> Handler的handleMessage()
12. Handler機制的應(yīng)用
12.1 在主線程中給子線程發(fā)送消息
public class MainActivity extends Activity {
private Handler subHandler;//是在子線程中創(chuàng)建的Handler對象
private Looper myLooper;//子線程中的Looper對象
private Handler handler = new Handler(){
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
* 匿名內(nèi)部類對象對外部類有一個隱式的強引用
*/
new Thread(new Runnable() {
@Override
public void run() {
// 1. 創(chuàng)建了Looper對象,然后Looper對象中創(chuàng)建了MessageQueue
// 2. 并將當(dāng)前的Looper對象跟當(dāng)前的線程(子線程)綁定ThreadLocal
Looper.prepare();
// 1. 創(chuàng)建了handler對象,然后從當(dāng)前線程中獲取Looper對象,然后獲取到MessageQueue對象
subHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
}
};
myLooper = Looper.myLooper();//獲取當(dāng)前線程中的Looper對象
/*
* 1. 從當(dāng)前線程中找到之前創(chuàng)建的Looper對象,然后找到 MessageQueue
* 2. 開啟死循環(huán),遍歷消息池中的消息
* 3. 當(dāng)獲取到msg的時候,調(diào)用這個msg的handler的disPatchMsg方法,讓msg執(zhí)行起來
*/
Looper.loop();
Log.d("tag", "loop()方法執(zhí)行完了");
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (myLooper!=null) {
myLooper.quit();
myLooper = null;
}
}
public void sendMsg(View view){
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);//模擬一個耗時操作
Message msg = new Message();
msg.what = 1;//區(qū)分發(fā)送的消息
msg.obj = "來自子線程的問候";
handler.sendMessage(msg);
}
}).start();
}
public void sendMsg2(View view) {
//從消息池中獲取一個舊的msg,如果沒有重新創(chuàng)建消息
subHandler.obtainMessage(2, "我是主線程發(fā)送來的祝福").sendToTarget();
}
}
12.2 同線程內(nèi)不同組件間的消息傳遞
Looper類用來管理特定線程內(nèi)對象之間的消息交換(MessageExchange)。你的應(yīng)用程序可以產(chǎn)生許多個線程。而一個線程可以有許多個組件,這些組件之間常常需要互相交換訊息。如果有這種需要,您可以替線程構(gòu)造一個Looper對象,來擔(dān)任訊息交換的管理工作。Looper對象會建立一個MessageQueue數(shù)據(jù)結(jié)構(gòu)來存放各對象傳來的消息(包括UI事件或System事件等)。每一個線程里可含有一個Looper對象以及一個MessageQueue數(shù)據(jù)結(jié)構(gòu)。在你的應(yīng)用程序里,可以定義Handler的子類別來接收Looper所送出的消息。
同線程不同組件之間的消息傳遞代碼如下:
/**
* ============================================================
* Copyright:${TODO}有限公司版權(quán)所有 (c) 2017
* Author: AllenIverson
* Email: 815712739@qq.com
* GitHub: https://github.com/JackChen1999
* 博客: http://blog.csdn.net/axi295309066
* 微博: AndroidDeveloper
* GitBook: https://www.gitbook.com/@alleniverson
* <p>
* Project_Name:HandlerDemo
* Package_Name:com.github.handlerdemo.activity
* Version:1.0
* time:2017/3/1 16:23
* des :
* gitVersion:2.12.0.windows.1
* updateAuthor:$Author$
* updateDate:$Date$
* updateDes:${TODO}
* ============================================================
*/
public classHandlerActivity extends Activity
{
private Button sendBtn;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
sendBtn=(Button)findViewById(R.id.send);
tv=(TextView)findViewById(R.id.textview);
sendBtn.setOnClickListener(newMyOnClickListener());
}
class MyOnClickListener implements OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send:
// 取得當(dāng)前線程的Looper,此時的線程為主線程(UI線程)
Looper looper = Looper.myLooper();
// 構(gòu)造一個Handler對象使之與Looper通信
MyHandler mHandler = newMyHandler(looper);
// 產(chǎn)生一個消息通過Handler傳遞給Looper
String msgStr = "main";
// 構(gòu)造一個消息,這里what參數(shù)設(shè)為1,obj參數(shù)設(shè)為msgStr變量。
Message msg = mHandler.obtainMessage(1, 1, 1, msgStr);
// 發(fā)送消息,調(diào)用Handler對象的handleMessage方法
mHandler.sendMessage(msg);
break;
}
}
}
// 自定義Handler類
class MyHandler extends Handler {
// 指定Looper對象來構(gòu)造Handler對象,而我們平時直接使用的Handler無參構(gòu)造方法實際上默認(rèn)是本線程的looper,可通過查看SDk源代碼了解。
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
tv.setText(String.valueOf(msg.obj));
break;
}
}
}
}
說明:此程序啟動時,當(dāng)前線程(即主線程, mainthread)已誕生了一個Looper對象,并且有了一個MessageQueue數(shù)據(jù)結(jié)構(gòu)。
調(diào)用Looper類別的靜態(tài)myLooper()函數(shù),以取得目前線程里的Looper對象。looper = Looper.myLooper ();
構(gòu)造一個MyHandler對象來與Looper溝通。Activity等對象可以藉由MyHandler對象來將消息傳給Looper,然后放入MessageQueue里;MyHandler對象也扮演Listener的角色,可接收Looper對象所送來的消息。mHandler = new MyHandler (looper);
先構(gòu)造一個Message對象,并將數(shù)據(jù)存入對象里。
Message msg = mHandler.obtainMessage(1, 1, 1, msgStr);
這里也可以這樣寫:
Message msg = new Message();
msg.what = 1;
msg.obj = msgStr;
- 通過mHandler對象將消息m傳給Looper,然后放入MessageQueue里。mHandler.sendMessage(msg);
此時,Looper對象看到MessageQueue里有消息m,就將它廣播出去,mHandler對象接到此訊息時,會調(diào)用其handleMessage()函數(shù)來處理,于是讓msgStr顯示于TextView上(更新UI)。
12.3 子線程傳遞消息給主線程
/**
* ============================================================
* Copyright:${TODO}有限公司版權(quán)所有 (c) 2017
* Author: AllenIverson
* Email: 815712739@qq.com
* GitHub: https://github.com/JackChen1999
* 博客: http://blog.csdn.net/axi295309066
* 微博: AndroidDeveloper
* GitBook: https://www.gitbook.com/@alleniverson
* <p>
* Project_Name:HandlerDemo
* Package_Name:com.github.handlerdemo.activity
* Version:1.0
* time:2017/3/1 16:23
* des :
* gitVersion:2.12.0.windows.1
* updateAuthor:$Author$
* updateDate:$Date$
* updateDes:${TODO}
* ============================================================
*/
public classHandlerActivity extends Activity
{
private Button sendBtn;
private TextView tv;
private MyHandler mHandler = null;
Thread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
sendBtn = (Button)findViewById(R.id.send);
tv = (TextView)findViewById(R.id.textview);
sendBtn.setOnClickListener(newMyOnClickListener());
}
class MyOnClickListener implements OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send:
thread = new MyThread();
thread.start();
break;
}
}
}
// 自定義Handler類
class MyHandler extends Handler {
// 指定Looper對象來構(gòu)造Handler對象,而我們平時直接使用的Handler無參構(gòu)造方法實際上默認(rèn)是本線程的looper,可通過查看SDk源代碼了解。
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg){
switch (msg.what) {
case 1:
tv.setText(String.valueOf(msg.obj));
break;
}
}
}
private class MyThread extends Thread {
@Override
public void run() {
// 獲得當(dāng)前線程的Looper對象
Looper curLooper =Looper.myLooper();
// 獲得主線程(UI線程)的Looper對象
Looper mainLooper =Looper.getMainLooper();
String msgStr;
if (curLooper == null) {
mHandler = newMyHandler(mainLooper);
msgStr = "curLooper isnull";
} else {
mHandler = newMyHandler(curLooper);
msgStr = "This iscurLooper";
}
Message msg =mHandler.obtainMessage(1, 1, 1, msgStr);
mHandler.sendMessage(msg);
}
}
}
Android會自動替主線程建立MessageQueue。在這個子線程里并沒有建立Message Queue。所以curLooper值為null,而mainLooper則指向主線程里的Looper。于是執(zhí)行mHandler= new MyHandler (mainLooper);此mHandler屬于主線程
mHandler.sendMessage(msg);就將msg消息存入到主線程的MessageQueue里
mainLooper看到Message Queue里有訊息,就會作出處理,于是由主線程執(zhí)行到mHandler的handleMessage()來處理消息。
13. Handler的核心代碼
public class Handler {
private Looper mLooper;
private MessageQueue mQueue;
public Handler(){
mLooper = Looper.myLooper();
if (mLooper == null){
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
public void handleMessage(){
}
public void dispatchMessage(Message msg){
handleMessage();
}
public boolean sendMessage(Message msg){
return sendMessageDelayed(msg,0)
}
public boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what,0);
}
public boolean sendEmptyMessageDelayed(int what, long delayMillis){
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg,0);
}
public boolean sendMessageDelayed(Message msg, long delayTime){
if (delayTime < 0) delayTime = 0;
return sendMessageAtTime(msg,SystemClock.uptimeMillis()+delayTime);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis){
MessageQueue queue = mQueue;
return enqueueMessage(queue,msg,uptimeMillis);
}
public boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){
msg.target = this;
return queue.enqueueMessage(msg, uptimeMillis);
}
public void post(Runnable r){
sendMessageDelayed(getPostMessage(r),0);
}
public static Message getPostMessage(Runnable r){
Message m = Message.obtain();
m.callback = r;
return m;
}
}
14. Looper的核心代碼
public class Looper {
static final ThreadLocal<Looper> mThreadLocal = new InheritableThreadLocal<>();
MessageQueue mQueue;
Thread mCurrentThread;
private static Looper sMainLooper;
private Looper() {
mQueue = new MessageQueue();
mCurrentThread = Thread.currentThread();
}
public static void prepare() {
if (mThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
mThreadLocal.set(new Looper());
}
public void prepareMainLooper(){
prepare();
sMainLooper = myLooper();
}
public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}
MessageQueue queue = me.mQueue;
for(;;){
Message msg = queue.next();//block
msg.target.dispatchMessage(msg);
}
}
public static Looper myLooper() {
return mThreadLocal.get();
}
}