面試的時(shí)候經(jīng)常會(huì)問handler原理啥的,前段時(shí)間剛好看了一個(gè)老師講handle機(jī)制酗失,老師講得很仔細(xì)清晰,這里我自己也用代碼模擬安卓handler實(shí)現(xiàn)一個(gè)基本線程通信。
說到handler就不得不說消息處理的五大組成部分:Message心赶,Handler,Message Queue缺猛,Looper和ThreadLocal缨叫。首先簡(jiǎn)要的了解這些對(duì)象的概念椭符;
Message:message就是一個(gè)數(shù)據(jù)模型吧,它的作用僅限于線程之間通信的時(shí)候傳遞消息耻姥,他可以攜帶少量數(shù)據(jù)销钝,用于線程之間傳遞信息,常用的四個(gè)字段target琐簇,what蒸健,obj,arg;
target:消息回調(diào)后的作用域類婉商,通常是一個(gè)handler似忧。
what:是一個(gè)區(qū)分不同消息的標(biāo)識(shí)符。
obj:這是obj是一個(gè)對(duì)象類型丈秩,可以攜帶自定義的類盯捌。
arg:int類型,攜帶的參數(shù)蘑秽。
下面貼出Message代碼:
public class Message {
Handler target;
public int what;
public Object obj;
@Override
public String toString() {
return obj.toString();
}
}
handler:它主要用于發(fā)送和接收消息饺著,有三個(gè)主要方法,這里實(shí)現(xiàn)基本功能肠牲,不探討具體細(xì)節(jié)
sendMessage()幼衰;
dispatchMessage();
handleMessage()缀雳;
它主要用于發(fā)送和處理消息的發(fā)送消息一般使用sendMessage()方法,還有其他的一系列sendXXX的方法渡嚣,但最終都是調(diào)用了sendMessageAtTime方法,把消息壓入消息隊(duì)列中。
而發(fā)出的消息經(jīng)過一系列的輾轉(zhuǎn)處理后俏险,最終會(huì)傳遞到Handler的handleMessage方法中严拒。這里我們handleMessage()方法在外部重寫,內(nèi)部實(shí)現(xiàn)調(diào)用竖独。
public class Handler {
private MessageQueue mQueue;
private Looper mLooper;
// Handler的初始化在主線程中完成
public Handler(){
//獲取主線程的looper對(duì)象
mLooper = Looper.myLooper();
this.mQueue = mLooper.mQueue;
}
// 發(fā)送消息裤唠,壓入隊(duì)列
public void sendMessage(Message msg){
msg.target = this;
mQueue.enqueueMessage(msg);
}
//內(nèi)部調(diào)用,外部實(shí)現(xiàn)
public void handleMessage(Message msg){
}
// 轉(zhuǎn)發(fā)
public void dispatchMessage(Message msg){
handleMessage(msg);
}
}
MessageQueue是消息隊(duì)列莹痢,主要存放所有handler發(fā)送過來的消息种蘸,這些消息會(huì)一直存放消息隊(duì)列中,等待被處理竞膳,每一個(gè)線程只有一直MessageQueue隊(duì)列航瞭。
這里實(shí)現(xiàn)的消息隊(duì)列里面有一個(gè)入棧和出棧函數(shù),這兩個(gè)函數(shù)的關(guān)系是一個(gè)生產(chǎn)者和消費(fèi)者的關(guān)系坦辟,我們?cè)贚ooper.loop方法中通過while死循環(huán)方法不斷檢測(cè)生產(chǎn)者方法刊侯,一旦消息隊(duì)列不為空,立即取出消息并處理锉走,同時(shí)消息的總數(shù)量也相應(yīng)需要改變滨彻。
public class MessageQueue {
//通過數(shù)組的結(jié)構(gòu)存儲(chǔ)message對(duì)象
Message[] items;
//入隊(duì)和出隊(duì)元素索引位置
int putIndex;
int takeIndex;
// 計(jì)數(shù)器
int count;
// synchronized (msg){} 代碼塊加鎖
// 互斥鎖
Lock lock;
// 條件變量
Condition notEmpty;
Condition notFull;
public MessageQueue(){
this.items = new Message[50];
this.lock = new ReentrantLock(); //這個(gè)鎖和sychronize還是有些區(qū)別的藕届,ReentrantLock 類實(shí)現(xiàn)了 Lock ,它擁有與synchronized 相同的并發(fā)性和內(nèi)存語義亭饵,但是添加了類似輪詢鎖休偶、定時(shí)鎖等候和可中斷鎖等候的一些特性。此外辜羊,它還提供了在激烈爭(zhēng)用情況下更佳的性能踏兜。(換句話說,當(dāng)許多線程都想訪問共享資源時(shí)八秃,JVM 可以花更少的時(shí)候來調(diào)度線程碱妆,把更多時(shí)間用在執(zhí)行線程上。)
this.notEmpty = lock.newCondition();
this.notFull = lock.newCondition();
}
// 加入隊(duì)列
// 生產(chǎn)
public void enqueueMessage(Message msg){
System.out.println("加入隊(duì)列");
try {
lock.lock();
//消息隊(duì)列滿了喜德,子線程停止發(fā)送消息山橄,阻塞
while (count == items.length){
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
items[putIndex] = msg;
//循環(huán)取值
putIndex = (++ putIndex == items.length)?0:putIndex;
count ++;
//生產(chǎn)出來新的message對(duì)象垮媒,通知主線程
notEmpty.signalAll();
}finally {
lock.unlock();
}
}
// 出隊(duì)列
// 消費(fèi)
public Message next(){
//消息隊(duì)列空了舍悯,子線程停止發(fā)送消息,阻塞
Message msg = null;
try {
lock.lock();
while (count == 0){
try {
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
msg = items[takeIndex];
items[takeIndex] = null; //元素重置空
takeIndex = (++takeIndex == items.length) ? 0 : takeIndex;
count --;
//使用列一個(gè)message對(duì)象睡雇,通知子線程萌衬,可以繼續(xù)生產(chǎn)
notFull.signalAll();
}finally {
lock.unlock();
}
return msg;
}
}
Looper:每個(gè)線程通過Handler發(fā)送的消息都保存在,MessageQueue中它抱,Looper通過調(diào)用loop()的方法秕豫,就會(huì)進(jìn)入到一個(gè)無限循環(huán)當(dāng)中,然后每當(dāng)發(fā)現(xiàn)Message Queue中存在一條消息观蓄,就會(huì)將它取出混移,并傳遞到Handler的handleMessage()方法中。每個(gè)線程中只會(huì)有一個(gè)Looper對(duì)象侮穿。
/**
* 一個(gè)線程對(duì)應(yīng)一個(gè)looper對(duì)象歌径,一個(gè)looper對(duì)應(yīng)一個(gè)消息隊(duì)列
*/
public final class Looper {
//每一個(gè)主線程都有一個(gè)looper對(duì)象
//Looper 對(duì)象保存在threadlocal中,保證列線程數(shù)據(jù)的隔離
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 一個(gè)looper對(duì)象 對(duì)應(yīng)一個(gè)消息隊(duì)列
MessageQueue mQueue;
private Looper(){
mQueue = new MessageQueue();
}
//Looper對(duì)象初始化
public static void prepare(){
if (sThreadLocal.get()!=null){
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
//獲取當(dāng)前線程的looper對(duì)象
public static Looper myLooper(){
return sThreadLocal.get();
}
//輪詢消息隊(duì)列
public static void loop(){
Looper me = myLooper();
if (me == null){
throw new RuntimeException("not Looper; Looper.prepare() wait call");
}
MessageQueue queue = me.mQueue;
for (;;){
Message msg = queue.next();
if (msg == null){
continue;
}
// 轉(zhuǎn)發(fā)給handler
msg.target.dispatchMessage(msg);
}
}
}
在Main方法中寫個(gè)測(cè)試?yán)?/h5>
public void test(){
//輪詢器初始化
Looper.prepare();
// 主線程當(dāng)中
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println(Thread.currentThread().getName() + ",receive:"+msg.toString());
}
};
for (int i=0;i<10;i++){
new Thread(){
@Override
public void run() {
while (true){
Message msg = new Message();
msg.what = 1;
synchronized (UUID.class){
msg.obj = Thread.currentThread().getName()+",send message"+ UUID.randomUUID().toString();
}
System.out.println(msg);
handler.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
//開啟輪詢
Looper.loop();
}
public void test(){
//輪詢器初始化
Looper.prepare();
// 主線程當(dāng)中
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println(Thread.currentThread().getName() + ",receive:"+msg.toString());
}
};
for (int i=0;i<10;i++){
new Thread(){
@Override
public void run() {
while (true){
Message msg = new Message();
msg.what = 1;
synchronized (UUID.class){
msg.obj = Thread.currentThread().getName()+",send message"+ UUID.randomUUID().toString();
}
System.out.println(msg);
handler.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
//開啟輪詢
Looper.loop();
}
打印結(jié)果如下:
// 08-18 19:13:28.149 8051-8129/com.dcw.handler I/System.out: Thread-5,send message41e677cd-0eb0-467b-a633-36a2e9c0cdc0
// 08-18 19:13:28.149 8051-8129/com.dcw.handler I/System.out: 加入隊(duì)列
// 08-18 19:13:28.149 8051-8136/com.dcw.handler I/System.out: Thread-12,send messageebd27bf8-b157-466a-bab5-8d6057334ed2
// 08-18 19:13:28.150 8051-8136/com.dcw.handler I/System.out: 加入隊(duì)列
// 08-18 19:13:28.150 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-5,send message41e677cd-0eb0-467b-a633-36a2e9c0cdc0
// 08-18 19:13:28.150 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-12,send messageebd27bf8-b157-466a-bab5-8d6057334ed2
// 08-18 19:13:28.154 8051-8137/com.dcw.handler I/System.out: Thread-13,send message3c1ae78b-8940-4a12-bb60-815442917edc
// 08-18 19:13:28.155 8051-8137/com.dcw.handler I/System.out: 加入隊(duì)列
// 08-18 19:13:28.155 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-13,send message3c1ae78b-8940-4a12-bb60-815442917edc
// 08-18 19:13:28.159 8051-8134/com.dcw.handler I/System.out: Thread-10,send messagef6beb349-975c-4e17-a475-d3cc7c5bd94f
// 08-18 19:13:28.159 8051-8134/com.dcw.handler I/System.out: 加入隊(duì)列
// 08-18 19:13:28.159 8051-8135/com.dcw.handler I/System.out: Thread-11,send message8d8ecf6e-3c76-4bad-b7af-dc916cb96b39
// 08-18 19:13:28.159 8051-8135/com.dcw.handler I/System.out: 加入隊(duì)列
// 08-18 19:13:28.160 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-10,send messagef6beb349-975c-4e17-a475-d3cc7c5bd94f
// 08-18 19:13:28.160 8051-8051/com.dcw.handler I/System.out: main,receive:Thread-11,send message8d8ecf6e-3c76-4bad-b7af-dc916cb96b39
// 08-18 19:13:29.149 8051-8130/com.dcw.handler I/System.out: Thread-6,send messagec7afaa49-e6b0-49a9
結(jié)果分析可以看到:只要子線程中一加入消息亲茅,那么looper就會(huì)輪詢從massagequeue中取出消息并且通過dispatchMessage發(fā)送到主線程中取執(zhí)行回铛。我個(gè)人覺得所謂主線程,只不過比子線程中多了一個(gè)looper克锣,我們的UI線程在只能在主線中刷新茵肃,就是應(yīng)為線程的loop方法不斷輪詢繪制的原因,子線程之所有不能刷新UI袭祟,是因?yàn)樽泳€程沒有l(wèi)oop方法验残,如果我們把子線程中設(shè)置一個(gè)looper,那么子線程也是可以刷新繪制UI的巾乳。
以上是我個(gè)人見解您没,歡迎大家斧正故俐。