一.場景
Handler 機制是Android異步消息的核心(線程間通信), 其實Handler不侷限於子線程與主線程(UI線程) ,我們完全可以創建一個子線程,然後初始化Looper,Handler,我們可以通過Handler在其他線程(包括主線程)往該子線程發送消息。例如系統幫我們封裝好的--> HandlerThread (一個封裝好Looper,MessageQueueu的線程(不死線程)),發送消息給子線程有什麼好處呢, 這裏拿HandlerThread說明,現在有個場景, 我們都知道UI線程不能做耗時的操作, 會影響程序的性能,用戶體驗,所以耗時(IO,網絡,數據庫操作)操作全部往子線程堆。怎麼解決呢, 我們可以創建一個子線程HandlerThread, 拿到該線程的Looper--HandlerThread.getLooper(),然後可以new Handler(Looper).post(Runnable r)往該線程發送消息, 然後就會在該子線程執行消息;
二. 重要的概念(Handler , Looper,MessageQueue,Message)
Handler 看看官方是怎麼說的吧 A Handler allows you to send and process Message
and
Runnable objects associated with a thread's MessageQueue ,簡單說Handler主要的作用就是往MessageQueue插入一個消息
Looper --Looper作用是從MessageQueue讀取Message,然後交給Handler處理消息,另外Looper還可以保證線程不死
MessageQueue 消息隊列, 鏈表插入和刪除方便, 主要用於存放消息(Message)
Message 消息(消息會包裝發送Message的Handler,詳細見下面 一一源碼驗證)
三.源碼解析(UI線程應用Handler機制)
<span style="color:#3333ff;"> </span>private static final TaskHandler TASK_HANDLER = new TaskHandler();
public void doClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message message = Message.obtain();
message.arg1 = ++i;
TASK_HANDLER.sendMessage(message);
}
}).start();
}
static final class TaskHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.i("Info", "message:"+msg.arg1);
}
}
上面代碼很簡單, 估計大家都會, 在子線程中睡三秒, 然後往UI線程發送一個消息。很簡單就完成了一個子線程和UI線程的通信,並且沒有任務問題, 其實,Android在背後爲我們做了很多封裝和處理了,纔可以往UI線程發送消息, 不信見如下:
new Thread(new Runnable() {
@Override
public void run() {
handler = new Handler();
handler.sendEmptyMessage(0x33);
}
}).start();
}
在子線程初始化一個Handler,然後發送一個消息。結果:
07-10 15:12:53.251: E/AndroidRuntime(5296): FATAL EXCEPTION: Thread-158
07-10 15:12:53.251: E/AndroidRuntime(5296): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
07-10 15:12:53.251: E/AndroidRuntime(5296): at android.os.Handler.<init>(Handler.java:121)
07-10 15:12:53.251: E/AndroidRuntime(5296): at com.example.just_intentservice.MainActivity$1.run(MainActivity.java:25)
07-10 15:12:53.251: E/AndroidRuntime(5296): at java.lang.Thread.run(Thread.java:856)
拋出異常了,大概意思是說沒有,不能在沒有初始化Looper的線程去創建Handler;
點進Handler源碼查看下,
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;
mCallback = null;
}
先會到當前線程查找Looper ,如果沒有Looper就會拋出上面的異常。所以不能在沒有初始化Looper的線程初始化Handler,UI線程之所以可以初始化Handler,我們見以下,Android UI線程是如何初始化Looper吧...
Java應用程序入口都是從mian函數, Android也不例外,只不過被SDK用hide標識着,我們無法查看,看下Android,UI線程是如何玩轉Handler機制的,在ActivityThread裏面main函數代碼如下
public static void main(String[] args) {
//******省略若干代碼****//
Looper.prepareMainLooper();//初始一個Looper
Looper.loop();//開始無限循環讀取消息
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我們代碼追蹤看Android是如何初始換Looper,在Looper類裏面
public static void prepareMainLooper() {//初始化MainLooper也是UI線程的Looper
prepare();
setMainLooper(myLooper());
}
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
private Looper() {
mQueue = new MessageQueue();//初始化一個MessageQueue
mThread = Thread.currentThread();
}
先判斷下當前線程是否存在Looper,存在拋出異常, 也就是說一個線程自能有一個Looper, 如果沒有,則new出一個Looer(在構造方法裏面初始化了一個對應的MessageQueue), 然後通過sThreadLocal保存起來, ThreadLocal用於線程保存對象 。然後來看下
setMainLooper(myLooper());
public static Looper myLooper() {
return sThreadLocal.get();
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
可以看出myLooper就是從當前線程中拿出Looper,然後返回,setMainLooper(),就更簡單了,其實就是保存個全局looper。就這樣UI線程就吧Looper初始化完成了,所以Handler可以正常的在主線程初始化;
下面我們來走一把完整的Handler機制通信流程
handler.sendEmptyMessage(0x33); <span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">----子線程</span>
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);// 包裝下時間調用兩個參數<span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">sendEmptyMessageDelayed方法</span>
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();//獲取一個Message
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;//如果時間小於0則賦值爲0
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {//對消息隊列判空
msg.target = this;//this表示當前handler
sent = queue.enqueueMessage(msg, uptimeMillis);//進入隊列
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
子線程的handler發送一個簡單的空消息到UI線程, 要經過不斷的包裝, 可以看出,最後都是包裝Message然後進入隊列(MessageQueue)其實post(Runnable r)最後也是經過包裝成Message對象,發送到隊列, 這裏就不做說明了, 詳細見源碼。 我們知道Looper在UI線程初始化完成就開始無限死循環讀取MessageQueue裏面消息,我們看Looper是如何讀取和處理消息的:
public static void loop() {
Looper me = myLooper();//獲取當前線程的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue; //獲取Looper裏面的消息隊列
while (true) { //死循環讀取
Message msg = queue.next(); // might block 從隊列裏面拿到消息
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
//省略一些代碼
msg.target.dispatchMessage(msg);//從message中拿到handler,然後調用<span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">dispatchMessage</span>
msg.recycle();//最終銷燬消息
}
}
}
Handler類裏面的dispatchMessage如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//callback其實是一個Runnable對象, 一般post(Runnable r)是執行
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//回調handlerMessage函數--一般該方法由我們繼承去實現的
}
}
private final void handleCallback(Message message) {
message.callback.run();//執行run方法體
}
說明下, 子線程handler.sendEmptyMessage()當Message到達MessageQueue,這裏就是子線程切換到UI線程了, 因爲MessageQueue跑在UI線程,上面代碼可以看出,Looper會不斷的在MessageQueue裏面讀取消息,然後讀到的message爲空的化return掉, 其實裏面還有一些喚醒機制(保證UI線程的不阻塞,當消息過來了才喚醒,這裏不細說)如果message不爲空,則從message中取出handler,然後調用handler的dispathMessage(Message
message)方法來方法消息, 如果消息爲post(Runnable r)則走handlerCallback,否則走handleMessage(msg),整個消息通信結束。
通過源碼分析, 我們完全可以創建一個不死子線程,然後在主線程中往子線程發送消息:
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == 0x88) {
Log.i("Info", "i am sub");
}
}
};
Looper.loop();
}
}).start();
}
public void doClick(View v) {
handler.sendEmptyMessage(0x88);
}
最後總結:Handler往MessageQueue插入Message,Looper往messageQueue裏面讀取message,最後都是交給Handler分發處理。