一、关于Handler
Handler对于我们Android开发者来说应该是再熟悉不过了,这也是在Android中最重要的消息机制,特别是在面试笔试时,Handler机制也是最常问到的话题。今天我们就来动手撸一个自己写的Handler,用java层代码方式来实现,进一步来了解Handler在线程通信过程中的作用。

二、问题
Handler机制也可以理解为线程间的消息机制,如果我们自己来设计Handler实现线程间通信,需要怎么做呢?我们知道,在Handler机制中,最重要的几个类:Handler、Looper、MessageQueue、Message、ThreadLocal。那它们在具体实现中又有什么作用呢?

三、思考
首先,从使用者角度来看,他的操作只有两步:

在主线程创建Handler实例,并重写handleMessage方法处理消息。

在子线程获取Handler的引用调用sendMessage方法发送消息,在handleMessage中即可处理该消息。

那从设计者角度来看,我们要分清Handler、Looper、MessageQueue、Message、ThreadLocal这几个类都担当了什么职责:

Handler 负责发送和处理消息

Looper 消息泵,也就是负责取出消息交给Handler来处理。

MessageQueue 消息队列,负责存取消息。

Message 具体发送的消息。

ThreadLocal 它主要用于做线程间的数据隔离用的,这里它在每个线程中存放各自对应的Looper。

好了,简单分析完各个类的作用,那我们开始挽起袖子撸代码吧。

四、实现
Picture

1、 Handler的实现

由于Handler主要负责发送和处理消息,那我们主要实现它的sendMessage、sendMessage、dispatchMessage三个方法,来处理消息的发送和接收:

public class Handler {
//消息队列
MessageQueue mQueue;
//Looper
Looper mLooper;

 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 final void sendMessage(Message msg){
     MessageQueue queue = mQueue;
     if (queue != null) {
         msg.target = this;
          queue.enqueueMessage(msg);
     }else {
         RuntimeException e = new RuntimeException(
             this + " sendMessage() called with no mQueue");
         throw e;
     }
 }

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    handleMessage(msg);
}

}
我们在Handler的构造函数中获取当前线程对应的looper,并取出Looper中对应的消息队列保存在成员变量中。sendMessage方法中我们给Message的target变量赋值为this,也就是表明了Message是由当前的Handler来负责处理的,之后调用enqueueMessage方法将消息存入消息队列中。而dispatchMessage方法我们实现比较简单,负责调用handleMessage来处理消息。

2、 Looper的实现

Looper主要负责取出消息交由Handler处理,我们主要来实现prepare、loop方法:

public class Looper {

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
MessageQueue mQueue;

private Looper() {
    mQueue = new MessageQueue();
}

public static Looper myLooper() {
    return sThreadLocal.get();
}

public static void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException(
                "Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper());
}

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();
        if (msg == null || msg.target == null)
            continue;
        //转发给handler
        msg.target.dispatchMessage(msg);
    }
}

}
在Looper的构造函数中我们创建了对应的消息队列来存取消息,并且在prepare方法中存入ThreadLocal当前线程的Looper,loop方法从当前线程的Looper的消息队列中取出消息,最终调用msg.target.dispatchMessage(msg)交友之前发送消息的Handler来处理消息。

3、Message的实现

Message的实现比较简单:

public final class Message {
//处理该消息的Handler
Handler target;
public int what;

public Object obj;

@Override
public String toString() {
    return obj.toString();
}

}
4、MessageQueue消息队列的实现

在消息队列的实现中我们主要考虑几个问题:

用什么数据结构存放消息,存放数据大小有限制。

当next()方法取出消息时,消息队列没有消息,该方法应阻塞。

当enqueueMessage方法存放消息时,消息大于存放消息限制大小,应阻塞。

//消息队列
public class MessageQueue {
//互斥锁
Lock lock;
//条件变量
Condition mEmptyQueue;
Condition mFullQueue;
//消息
Message[] mMessages;
//装入 和取出消息的下标
int putIndex;
int takeIndex;
//记录数 用于判断是否继续生产和消费
int count;
public MessageQueue(){
//初始化50个消息
mMessages = new Message[50];
lock = new ReentrantLock();
//标示
mEmptyQueue = lock.newCondition();
mFullQueue = lock.newCondition();
}
//生产者 子线程
final void enqueueMessage(Message msg){
//添加至消息队列
try{
lock.lock();
while(count == mMessages.length){
try {
mFullQueue.await();
} catch (Exception e) {
e.printStackTrace();
}
}
mMessages[putIndex] = msg;
putIndex = (++putIndex == mMessages.length ? 0 : putIndex);
count++;
//通知主线程继续执行
mEmptyQueue.signalAll();
}finally{
lock.unlock();
}

}
//消费者  主线程
final Message next(){
    //取出消息
    Message message = null;
    try{
        lock.lock();
        //取到最后一个
        while (count == 0) {
            try {
                mEmptyQueue.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
         message = mMessages[takeIndex];
         mMessages[takeIndex] = null;
        takeIndex = (++takeIndex == mMessages.length ? 0 : takeIndex);
        count--;
        //通知子线程   
        mFullQueue.signalAll();
    }finally{
        lock.unlock();
    }
    return message;
}

}
这里的next和enqueueMessage是典型的生产者、消费者的关系,为防止出现错乱我们给两个方法都加上Lock锁,当enqueueMessage方法存放消息时如果当前队列消息满了,则调用mFullQueue.await();进行等待消息处理,当向消息队列中存放消息后,也就是说消息队列不为空了,调用mEmptyQueue.signalAll();通知next()方法来处理消息。

至此,我们的Handler消息处理过程已经基本完成了,下面我们测试下看看:

5、测试

public class Test {

public static void main(String[] args) {
    //初始化Looper
    Looper.prepare();

    final Handler hander = new Handler(){
        public void handleMessage(Message msg) {
            System.out.println(Thread.currentThread().getName() + "--receiver--" + msg.toString());
        };
    };

    for (int i = 0; i < 10; i++) {
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    Message msg = new Message();
                    msg.what = 0;
                    synchronized (UUID.class) {
                        msg.obj = Thread.currentThread().getName()+"--send---"+UUID.randomUUID().toString();
                    }
                    System.out.println(msg);
                    hander.sendMessage(msg);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
    //开始消息循环
    Looper.loop();
}

}
看下测试结果:

Thread-0–send—e4ed9a81-3477-4f5d-a663-1d30626d93b5
Thread-8–send—4c403131-ec14-406c-b57b-a4943dfa93ac
Thread-7–send—9752a85d-9517-4607-a54f-92c2342b7a28
Thread-6–send—7d4ee443-3ab5-4c4e-aac5-eeafc26b78d9
Thread-9–send—70ba7292-1ff4-404d-974e-dfedb1a3fa71
Thread-4–send—614e07e6-bc39-45be-93b0-6996de7f159e
Thread-2–send—7bfaa831-a31b-457a-82cd-145a9d98d351
Thread-5–send—8ffd7327-6ddb-4088-93e6-1304fc926814
Thread-1–send—f6d5e373-88b0-44e9-ab51-f95808acb068
main–receiver–Thread-0–send—e4ed9a81-3477-4f5d-a663-1d30626d93b5
main–receiver–Thread-8–send—4c403131-ec14-406c-b57b-a4943dfa93ac
main–receiver–Thread-7–send—9752a85d-9517-4607-a54f-92c2342b7a28
main–receiver–Thread-6–send—7d4ee443-3ab5-4c4e-aac5-eeafc26b78d9
main–receiver–Thread-9–send—70ba7292-1ff4-404d-974e-dfedb1a3fa71
main–receiver–Thread-4–send—614e07e6-bc39-45be-93b0-6996de7f159e
main–receiver–Thread-2–send—7bfaa831-a31b-457a-82cd-145a9d98d351
main–receiver–Thread-5–send—8ffd7327-6ddb-4088-93e6-1304fc926814
Thread-3–send—56f8b613-99fa-4ef2-a4b9-c762c4d0cd27
main–receiver–Thread-1–send—f6d5e373-88b0-44e9-ab51-f95808acb068
main–receiver–Thread-3–send—56f8b613-99fa-4ef2-a4b9-c762c4d0cd27
测试成功!!我们自己的Handler也可以正常处理消息啦~

五、总结
Handler源码的实现过程要比我们自己的复杂很多,特别是消息处理的细节,调用了底层C++的代码。但实现的整体思路和我们是一样的,通过动手实践一次,加深对Handler的理解,对我们认识和处理消息机制的问题大有裨益。