Handler機制簡介
Handler是android中最重要組成部分之一,Handler機制可以看做是一個消息阻塞隊列,APP啟動後很快就進入死循環(while循環),不斷的讀取消息隊列中的消息,每個線程最多隻有一個消息隊列,沒有消息時就阻塞,有就立馬執行。所有消息排隊執行,因為是一個線程,是以同時隻能執行一個消息。android的view繪制,事件響應(點選,觸摸螢幕等)都是把消息發送到了主線程的消息隊列,等待android APP的執行(這點可以通過手動抛出異常檢視錯誤堆棧來驗證)。包括自己在主線程new 的handler最終也是把消息插入到了主線程消息隊列中。從以上來看android主線程大部分時間是空閑的。當點選螢幕後手機能立馬響應也可以看出android主線程大部分時間是空閑的。雖然主線程要處理的事情狠多,很雜,很瑣碎(view布局、繪制,事件分發等等),但處理時間都很短暫,可以保證很快處理完畢,然後等待下一個消息的到來。android handler機制簡可以實作所有view相關的操作都在主線程進行,進而避免了使用 鎖 。具體實作代碼 如下。
java工程實作Handler機制代碼
下面的代碼跟android的handler機制主要原理完全一緻,但不依賴android系統。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
| import com.handler.Handler;
import com.handler.Looper;
import com.handler.Message;
public
class Main {
public static void main(String[] args) {
new Main().start();
}
private void start(){
//建立該線程唯一的消息隊列,線程安全的阻塞隊列
Looper.prepare();
onCreate();
//死循環,阻塞式,執行下面代碼後主線程就會去擷取消息隊列裡的消息,沒有消息時就阻塞,有就執行。執行Looper.loop前即使消息隊列裡有消息,消息也不會執行,因為主線程還沒有去檢查消息隊列。
Looper.loop();
//下面 的代碼通常不會執行,除非手動讓主線程消息隊列退出。退出主線程消息隊列後android的view布局、繪制,事件分發就不執行了,是以android APP也沒必要繼續執行了,是以android采用了抛出異常的方式結束APP。
System.out.println(
"exit........");
throw
new RuntimeException(
"Main thread loop unexpectedly exited");
}
private void onCreate() {
//
// 下面的操作相當于運作在android的UI線程中
//
final Thread thread = Thread.currentThread();
System.out.println(
"main thread=" + thread);
Handler handler =
new Handler() {
@Override
public void handleMessage(Message msg) {
//若thread == Thread.currentThread(),則證明已經運作在主線程中了
System.out.println(
"current thread is main thread? " + (thread == Thread.currentThread()));
System.out.println(msg);
System.out.println();
}
};
// 測試1 主線程建立handler,子線程使用該handler發送消息
new Thread() {
public void run() {
try {
//模拟耗時操作
Thread.sleep(
1000 *
2);
}
catch (InterruptedException e) {
}
Message message =
new Message();
message.obj =
"new Thread" + Thread.currentThread();
message.what = (
int) System.currentTimeMillis();
//在子線程中發送消息
handler.sendMessage(message);
try {
Thread.sleep(
1000 *
2);
}
catch (InterruptedException e) {
}
message =
new Message();
message.obj =
"hanler...waht==1" ;
message.what =
1;
//在子線程中發送消息
handler.sendMessage(message);
message =
new Message();
message.obj =
"hanler...waht==2" ;
message.what =
2;
//在子線程中發送消息
handler.sendMessage(message);
message =
new Message();
message.obj =
"hanler...waht==3" ;
message.what =
3;
//在子線程中發送消息
handler.sendMessage(message);
};
}.start();
// 測試2 在thread内部建立handler,結果會抛出異常
new Thread() {
public void run() {
try {
sleep(
1000 *
3);
}
catch (InterruptedException e) {
}
/*
* 線上程内部使用預設構造函數建立handler會抛出異常。
* android中也可以在子線程中建立Handler,但要在初始化時傳入Looper,
* Looper.getMainLooper()擷取到的就是主線程的Looper,是以可以這樣建立
*
* new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
//運作在主線程中
}
};
*/
Handler h =
new Handler() {
public void handleMessage(Message msg) {
System.out.println(
"haneler msg...." + msg);
};
};
Message message =
new Message();
message.obj =
"handler in new Thread";
message.what = (
int) System.currentTimeMillis();
//在子線程中發送消息
h.sendMessage(message);
};
}.start();
//
// 上面的操作相當于運作在android的UI線程中
//
}
}
|
運作結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| main thread=Thread[main,5,main]
current thread is main thread? true
what=18175614 obj=new ThreadThread[Thread-0,5,main]
Exception in thread "Thread-1" java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at com.handler.Handler.<init>(Handler.java:14)
at Main$3$1.<init>(Main.java:103)
at Main$3.run(Main.java:103)
current thread is main thread? true
what=1 obj=hanler...waht==1
current thread is main thread? true
what=2 obj=hanler...waht==2
current thread is main thread? true
what=3 obj=hanler...waht==3
|
Handler代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| package com.handler;
public
class Handler {
private MessageQueue messageQueue;
public Handler() {
Looper looper=Looper.myLooper();
if (looper==
null) {
throw
new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
this.messageQueue=looper.messageQueue;
}
public void sendMessage(Message msg) {
//Looper循環中發現message後,調用message.targer就得到了目前handler,使用taget.handleMessage
//就把消息轉發給了發送message時的handler的handleMessage函數
msg.target=
this;
messageQueue.enqueueMessage(msg);
}
public void handleMessage(Message msg) {
}
}
|
Looper代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| package com.handler;
public
class Looper {
private
static
final ThreadLocal<Looper> threadLocal=
new ThreadLocal<>();
/**
* 存儲Message的隊列,阻塞式,沒有消息則一直等待
*/
final MessageQueue messageQueue;
private Looper() {
messageQueue=
new MessageQueue();
}
/**為該線程建立Looper,
* 若該線程已經有Looper了則不需要再次調用prepare
*/
public static void prepare() {
if (threadLocal.get() !=
null) {
throw
new RuntimeException(
"Only one Looper may be created per thread");
}
threadLocal.set(
new Looper() );
}
public static void loop() {
Looper looper=myLooper();
if (looper ==
null) {
throw
new RuntimeException(
"No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue messageQueue=looper.messageQueue;
for(;;){
Message message=messageQueue.next();
message.target.handleMessage(message);
}
}
/**
* 擷取當先線程的Looper
*
@return
*/
public static Looper myLooper() {
return threadLocal.get();
}
}
|
MessageQueued代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| package com.handler;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public
class MessageQueue {
private BlockingQueue<Message>blockingQueue=
new LinkedBlockingQueue<>();
/**
* 阻塞式,沒有消息則一直等待
*
@return
*/
public Message next() {
try {
return blockingQueue.take();
}
catch (InterruptedException e) {
throw
new RuntimeException();
}
}
/**
* 插入到消息隊列尾部
*
@param message
*/
void enqueueMessage(Message message) {
try {
blockingQueue.put(message);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
|
ThreadLocal簡單實作
ThreadLocal内部原理和下面實作方式不同,但達到的效果是相同的,本篇主要介紹Handler機制,簡化了ThreadLocal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| package com.handler;
import java.util.HashMap;
import java.util.Map;
/**
* ThreadLocal簡單實作
*
@author Young
*
*
@param <T>
*/
public
class ThreadLocal<T> {
private Map<Thread,T>map;
public ThreadLocal() {
map=
new HashMap<>();
}
public void set(T obj) {
map.put(Thread.currentThread(),obj);
}
public T get() {
return map.get(Thread.currentThread());
}
}
|
Message代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| package com.handler;
public
class Message {
Handler target;
public Object obj;
public
int what;
@Override
public String toString() {
return
"what="+what+
" obj="+obj.toString();
}
}
|
以上就是android Handler機制原理代碼了。
android還提供了HandlerThread,其實是對Handler和Thread的封裝。
先看一下HandlerThread使用方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| Handler myHandler;
new HandlerThread(
"Compress-Thread") {
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
myHandler =
new Handler();
myHandler.post(
new Runnable(){
@Override
public void run() {
//在HandlerThread線程執行
}
});
}
}.start();
//不要在這使用myHandler發送消息,因為myHandler是在onLooperPrepared中建立的,onLooperPrepared又是運作在HandlerThread線程的,是以剛執行到這時HandlerThread線程可能還沒有建立完,onLooperPrepared也就不會執行,myHandler自然是null
|
每次new HandlerThread都會建立一個新線程,當我們使用myHandler發送消息時消息就會在HandlerThread線程執行。HandlerThread線程内部也是一個死循環,通常不會退出,當通過myHandler為其發送消息時就會從阻塞中醒來執行消息,執行完消息隊列裡的消息後就又阻塞。HandlerThread可以排隊執行消息,保證能按加入消息的先後順序執行。比如我們需要壓縮很多圖檔時,就可以使用HandlerThread,主線程直接把圖檔通過myHandler發送到HandlerThread,HandlerThread就可以執行圖檔壓縮,所有壓縮任務都按添加順序依次執行。
來自我的部落格
http://blog.csdn.net/qingchunweiliang/article/details/50448365