核心知識點
1、相關名詞
- UI線程:就是我們的主線程,系統在創建UI線程的時候會初始化一個Looper對象,同時也會創建一個與其關聯的MessageQueue
- Handler:作用就是發送與處理信息,如果希望Handler正常工作,在當前線程中必須要有一個Looper對象
- Message:Handler接收與處理的對象。Handler也能接收與處理Runnable對象
- MessageQueue:消息隊列,先進先出管理Message,在初始化Looper對象時會創建一個與之關聯的MessageQueue
- Looper:每個線程只能夠有一個Looper,Looper負責創建並管理當前線程中的MessageQueue,調用loop方法后就會在一個無限循環體中不斷地從MessageQueue中取出Message並分發給對應的Handler,最后回調handleMessage()方法處理此消息。Looper才是整個機制的核心!
2、基本使用過程:
- 在主線程中創建Handler並重寫handleMessage()方法
- 在任何線程中都可以利用此Handler發送消息,消息會被發送到主線程的MessageQueue中
- 一旦MessageQueue中有新消息,主線程中的 Looper 就會發現此消息,然后就會調用Handler的handleMessage()方法處理此消息
3、一些細節:
- 調用Looper.prepare()后首先會在本線程中保存唯一的一個Looper實例,然后會在該實例中創建並保存一個MessageQueue對象;Looper.prepare()在一個線程中只能調用一次,MessageQueue在一個線程中也只存在一個。
- 調用Looper.loop()方法后會讓當前線程進入一個無限循環中,Looper不斷從MessageQueue中讀取消息,然后回調msg.target.dispatchMessage(msg)方法。
- Handler的構造方法中,會首先得到當前線程中保存的Looper實例,進而與Looper實例中的MessageQueue相關聯。
- Handler的sendMessage方法,會給msg.target賦值為handler自身,然后加入MessageQueue中。
- 在構造Handler實例時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。
Handler面試點
1、Handler的產生背景
Handler是線程間通訊的機制,Android中,網絡訪問、文件處理等耗時操作必須放到子線程中去執行,否則將會造成ANR異常。
ANR異常:Application Not Response 應用程序無響應
產生ANR異常的原因:在主線程執行了耗時操作,對Activity來說,主線程阻塞5秒將造成ANR異常,對BroadcastReceiver來說,主線程阻塞10秒將會造成ANR異常。
解決ANR異常的方法:耗時操作都在子線程中去執行
但是,Android不允許在子線程去修改UI,可我們又有在子線程去修改UI的需求,因此需要借助Handler(但是,一定要明白,
Handler的作用不是為了在子線程去修改UI,而是為了實現線程間通訊!一定要明白理念與表現的區別)。
2、Handler機制相關概念
- Looper:一個線程可以產生一個Looper對象,由它來管理此線程里的MessageQueue(消息隊列)。
- Handler:你可以構造Handler對象來與Looper溝通,以便push新消息到MessageQueue里;或者接收Looper從Message Queue取出發送來的消息。
- Message Queue(消息隊列):用來存放線程放入的消息。
- 線程:UIthread 通常就是main thread,而Android啟動程序時會替它建立一個MessageQueue。
3、基本過程:
UI線程創建時,就創建了一個Looper,Looper內部維護這一個MessageQueue。
Looper通過開啟一個while(true)死循環來輪詢MessageQueue中的Message。
當Looper輪詢到Message時,就分發此Message。
4、Looper和Handler之間的關聯
Handler在子線程發送消息到MessageQueue,Message被Looper取出來后,分發給handler的handleMessage方法來處理。
那么,為什么說一個Looper可以對應多個Handler,Looper如何保證哪個Handler發出去的Message將交由哪個handler來處理?
因為Handler在發送Message的時候,在Message的成員變量target上標記了當前handler的引用:
message.target = this;//this即這個發送此Message的handler對象
當Looper取到message時,通過下面的方法分發Message:
message.target.dispatchMessage(message); //message.target即發送此Message的handler對象
因此,哪個handler發送的Message,將由哪個Handler來處理此Message。

Looper詳解
prepare()方法
public static final void prepare() {
if (sThreadLocal.get() != null) throw new RuntimeException("Only one Looper may be created per thread");
sThreadLocal.set(new Looper(true));
}
下面看其構造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
在構造方法中,創建了一個消息隊列MessageQueue。
loop()方法
public static void loop() {
final Looper me = myLooper();//方法體為 return sThreadLocal.get(); 即此方法直接返回了sThreadLocal存儲的Looper實例
if (me == null) throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");//如果me為null則拋出異常,也就是說looper方法必須在prepar()之后運行。
final MessageQueue queue = me.mQueue;//拿到該looper實例中的消息隊列
Binder.clearCallingIdentity(); // Make sure the identity of this thread is that of the local process, and keep track of what that identity token actually is.
final long ident = Binder.clearCallingIdentity();
//無限循環
for (;;) {
Message msg = queue.next(); // might block,取出一條消息,如果沒有消息則阻塞等待
if (msg == null) return;
Printer logging = me.mLogging; // This must be in a local variable, in case a UI event sets the logger
if (logging != null) logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
msg.target.dispatchMessage(msg);//Msg的target其實就是handler對象
if (logging != null) logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
final long newIdent = Binder.clearCallingIdentity();// Make sure that during the course of dispatching the identity of the thread wasn't corrupted.
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.recycle();//釋放消息占據的資源
}
}
Looper主要作用:
- 1、與當前線程綁定,保證一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue。
- 2、執行loop()方法,不斷從MessageQueue中取消息,交給消息的target屬性的dispatchMessage方法去處理。
大家可能還會問,在Activity中,我們並沒有顯示的調用Looper.prepare()和Looper.loop()方法,為啥Handler可以成功創建呢?
這是因為在Activity的啟動代碼中,已經默默的在當前UI線程中調用了Looper.prepare()和Looper.loop()方法。
而如果我們要想在子線程中創建Handler,則必須先調用Looper.prepare()方法,Handler創建后再調用Looper.loop()方法。
Handler詳解
使用Handler之前,我們都是初始化一個實例,我們可以在聲明的時候直接初始化,或者在onCreate中初始化。
我們首先看Handler的構造方法,看其如何與MessageQueue聯系上的,它在子線程中發送的消息(當然可以在任何線程)是怎么發送到MessageQueue中的。
public Handler() {//我們一般都是使用此無參的構造方法
this(null, false);
}
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());
}
}
mLooper = Looper.myLooper();//獲取當前線程保存的Looper實例
if (mLooper == null) throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");//在初始化Handler之前必須先通過Looper.prepare()方法創建Looper的實例
mQueue = mLooper.mQueue;//獲取這個Looper實例中保存的MessageQueue(消息隊列)
mCallback = callback;
mAsynchronous = async;
}
然后看我們最常用的發送消息相關的幾個方法
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) delayMillis = 0;
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
輾轉反側最后都是調用了sendMessageAtTime方法,在此方法內部又直接獲取MessageQueue,然后調用了enqueueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) msg.setAsynchronous(true);
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage中首先為meg.target賦值為this,最后調用queue的enqueueMessage的方法,也就是說handler發出的消息,最終會保存到消息隊列中去。【上面我們說了,Looper的loop方法會取出每個msg然后交給msg.arget.dispatchMessage(msg)去處理消息】,也就是說Looper的loop方法會把取出的每個msg通過當前的handler的dispatchMessage回調處理。
下面我們去看一看handler中的這個dispathMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) handleCallback(msg);
else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) return;
}
handleMessage(msg);
}
}
可以看到,最后調用了handleMessage方法,可實際上這是一個空方法,為什么呢?因為消息的最終回調是由我們控制的,我們在創建handler的時候都是復寫handleMessage方法,然后根據msg.what進行消息處理。
Handler的全部方法
構造方法
其他方法
獲取消息
移除消息和回調
發送消息
post方法![]()
有時候為了方便,我們會直接寫如下代碼:
mHandler.post(new Runnable() { @Override public void run() { mTxt.setText("yoxi");//在run方法中可以更新UI } });
其實這個Runnable並沒有創建什么線程,而是發送了一條消息,下面看源碼:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain();//得到了一個Message對象 m.callback = r;//將我們創建的Runable對象作為callback屬性,賦值給了此Message return m; }
后面的步驟就和handler.sendMessage一樣了可以看到,這里msg的callback和target都有值,那么會執行哪個呢?其實上面已經貼過代碼,就是dispatchMessage方法:如果msg.callback不為null,則執行callback回調,也就是我們定義的Runnable。
演示代碼
public class HandlerTestActivity extends ListActivity {
private TextView tv_info;
private Handler uiHandler;
private StaticThread thread;//一個子線程
public static final int MSG_WHAT_1 = 1;
public static final int MSG_WHAT_2 = 2;
public static final int MSG_WHAT_3 = 3;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"開啟子線程,並在子線程中創建一個Handler", //
"在主線程中,通過子線程的Handler[向子線程]發消息", //
"演示Handler的post方法"};
tv_info = new TextView(this);
tv_info.setText("Handler、Looper、Message、MQ、Thread關系");
getListView().addFooterView(tv_info);
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))));
uiHandler = new StaticUiHandler(this); //系統啟動時已經為主線程初始化了Looper、MQ等,我們可以直接創建Handler
thread = new StaticThread(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (uiHandler != null) uiHandler.removeCallbacksAndMessages(null);
if (thread != null && thread.getAnsyHandler() != null) thread.getAnsyHandler().removeCallbacksAndMessages(null);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0://開啟子線程,並在子線程中創建一個Handler
if (thread != null && !thread.isAlive()) thread.start();//A thread is alive if it has been started and has not yet died.
break;
case 1://在主線程中,通過子線程的Handler[向子線程]發消息
Message msg = Message.obtain(null, MSG_WHAT_1, "消息內容"); //第一個參數Handler的作用是指定msg.target
//這里設為null的原因是:后面調用sendMessage方法時重新指定了發送此消息的Handler為msg.target
if (thread != null && thread.getAnsyHandler() != null) thread.getAnsyHandler().sendMessage(msg);
tv_info.append("\n1、在UI線程中用子線程的Handler發消息,what=" + msg.what);
break;
case 2:
//其實這個Runnable並沒有創建什么線程,而是發送了一條消息,當Handler收到此消息后回調run()方法
uiHandler.post(() -> tv_info.append("\n演示Handler的post方法"));
break;
}
}
//***********************************************靜態內部類,防止內存泄漏*******************************************
/**
* 主線程使用的Handler
*/
private static class StaticUiHandler extends Handler {
private SoftReference<HandlerTestActivity> mSoftReference;
public StaticUiHandler(HandlerTestActivity activity) {
mSoftReference = new SoftReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerTestActivity activity = mSoftReference.get();
if (activity != null && activity.thread != null && activity.thread.getAnsyHandler() != null) {
activity.tv_info.append("\n4、UI線程的Handler收到消息,what=" + msg.what);
Message msg3 = Message.obtain(null, MSG_WHAT_3, msg.obj);
activity.thread.getAnsyHandler().sendMessageAtTime(msg3, SystemClock.uptimeMillis() + 2000);
activity.tv_info.append("\n5、在UI線程中用子線程的Handler發消息,what=" + msg3.what);
}
}
}
/**
* 異步線程(子線程)使用的Handler
*/
private static class StaticAnsyHandler extends Handler {
private SoftReference<HandlerTestActivity> mSoftReference;
public StaticAnsyHandler(HandlerTestActivity activity) {
mSoftReference = new SoftReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerTestActivity activity = mSoftReference.get();
if (activity != null) {
final Message tempMsg = Message.obtain(msg);//把收到的消息保存起來
//注意,一定要注意!根據消息池機制,當此消息不再在【此子線程】中使用時,此msg會立即被重置(引用雖在,內容為空)
//所以,如果想把此消息轉發到其他線程,或者想在其他線程中引用此消息,一定要手動把消息保存起來!
activity.runOnUiThread(() -> {//在子線程中創建Handler的目的是為了和其他線程通訊,絕對不是(也不能)更新UI
activity.tv_info.append("\n2、子線程的Handler收到消息,what=" + tempMsg.what);
if (activity.uiHandler != null && tempMsg.what == MSG_WHAT_1) {
Message msg2 = Message.obtain(null, MSG_WHAT_2, tempMsg.obj);
activity.uiHandler.sendMessageDelayed(msg2, 2000);
//注意,不能直接把一條還在使用的消息轉發出去,否則IllegalStateException: This message is already in use
activity.tv_info.append("\n3、在子線程中用UI線程的Handler發消息,what=" + msg2.what);
}
});
}
}
}
/**
* 一個線程,用於執行耗時的操作
*/
private static class StaticThread extends Thread {
private SoftReference<HandlerTestActivity> mSoftReference;
public StaticThread(HandlerTestActivity activity) {
mSoftReference = new SoftReference<>(activity);
}
private Handler ansyHandler;
public Handler getAnsyHandler() {
return ansyHandler;
}
public void run() {
HandlerTestActivity activity = mSoftReference.get();
if (activity != null) {
Looper.prepare(); //在創建Handler【前】必須調用此方法初始化Looper,否則直接報否則報RuntimeException崩潰
//里面做的事情:①為當前線程創建唯一的Looper對象 ②在它的構造方法中會創建一個的MessageQueue對象
//此方法只能被調用一次,這保證了在一個線程中只有一個Looper實例以及只有一個與其關聯的MessageQueue實例
ansyHandler = new StaticAnsyHandler(activity); //任何線程都可通過此Handler發送信息!
Looper.loop(); //若要能夠接收到消息,創建Handler后,必須調用loop方法。當然此方法必須是在prepar之后執行
//里面做的事情:啟動一個死循環,不斷從MQ中取消息,沒有則阻塞等待,有則將消息傳給指定的Handler去處理
activity.runOnUiThread(() -> Toast.makeText(activity, "會一直阻塞在這里", Toast.LENGTH_SHORT).show());
}
}
}
}
2017-8-25