在Handler全解析之源码分析+手把手带你写Handler(上)中我们分析了Handler的源码,了解到Handler, MessageQueue, Looper,ThreadLocal,Message之间的关系,消息是如何取出,如何存入的。为了加深记忆,以及方便吹牛逼,今天给大家演示一波如何手写Handler(当然是简易版),重点还是加强对Handler的理解,话不多说,开始吧。
我们知道主线程Looper的创建和开启轮询都是在ActivityThread中开始的,所以我们先创建一个ActivityThread.java
import org.junit.Test;
import padphone.myapplication.handler.core.Handler;
import padphone.myapplication.handler.core.Looper;
import padphone.myapplication.handler.core.Message;
public class ActivityThread {
@Test
public void main() {
//调用Looper.prepare()为当前线程创建一个Looper
Looper.prepare();
//创建Handler发送/接收消息
final Handler handler = new Handler() {
@Override
protected void handleMessage(Message message) {
super.handleMessage(message);
System.out.println("result=" + message.obj.toString()
);
}
};
//开启一个线程,发送消息
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "hello handler";
handler.sendMessage(message);
}
}).start();
//开启轮询
Looper.loop();
}
}
逻辑很简单,我们先按照我们的想象把代码写出来,具体Looper,Message,Hanlder, MQ的实现我们一步一步的来。
首先要关心的是Looper类,它要做4件事:
有了思路代码就很好写了,来看看Looper.java的具体实现:
package padphone.myapplication.handler.core;
public class Looper {
//创建一个ThreadLocal全局遍历,保存Looper
private static ThreadLocal mThreadLocal = new ThreadLocal<>();
//Looper中需要持有MessageQueue的引用
public MessageQueue mQueue;
private Looper(){
//在私有构造方法中创建MessageQueue
mQueue = new MessageQueue();
}
//创建Looper并保存到threadlocal中,要保证一个线程只有一个Looper
public static void prepare() {
if (mThreadLocal.get()!=null){
throw new RuntimeException("only one looper in thread");
}
mThreadLocal.set(new Looper());
}
//对外提供获取当前线程Looper的方法
public static Looper myLooper(){
return mThreadLocal.get();
}
//轮询MessageQueue,并通过handler分发消息
public static void loop() {
Looper looper = myLooper();
for (;;){
//不断在MessageQueue中取数据
Message message = looper.mQueue.next();
if (message!=null){
//分发消息
message.target.dispatchMessage(message);
}
}
}
}
我们已经知道的是Message中一定会有一个target,一个obj属性,至于其它的一些属性和方法因为在这里不是必须的先省略吧
public class Message {
public Handler target;
public Object obj;
}
MessageQueue至少有两个方法,消息的入队和出队。android源码中使用了epoll模型来解决while(true)循环资源抢占的问题,调用的是底层的native方法,我们没有这个条件,只能用BlockingQueue来替代了。
BlockingQueue有两个特点:
由于在队列为空时线程会处于等待状态,所以即便我们用while(true)也不会造成死循环,当然了while(true)已经在Looper中用到,我们在MessageQueue中只需要关心消息入队和出队
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class MessageQueue {
//使用ArrayBlockingQueue来装载消息体,默认数量为50
private BlockingQueue mBlockingQueue = new ArrayBlockingQueue<>(50);
//消息入队
public void enqueueMessage(Message message) {
try {
mBlockingQueue.put(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//从BlockingQueue中取出消息
public Message next() {
try {
return mBlockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
最重要的Handler终于来了,但是依然很简单。我们先回顾一下android源码中Handler做了什么事情
虽然sendMessage的方式多种多样,为了方便我们就只写一个sendMessage(Message msg)。在dispatchMessage中有三种回调方式,我们也只取最简单的一种,直接调用本身的handleMessage(Message msg)方法。下面来看一下具体的代码实现:
public class Handler {
private MessageQueue mQueue;
private Looper mLooper;
public Handler(){
//在构造方法中获取Looper对象
mLooper = Looper.myLooper();
//根据looper获取到MessageQueue
mQueue = mLooper.mQueue;
}
//发送消息
public void sendMessage(Message message){
enqueueMessage(message);
}
private void enqueueMessage(Message message){
//给message.target赋值
message.target = this;
//调用MQ中的enqueueMessage方法,将消息放入队列
mQueue.enqueueMessage(message);
}
//分发消息
void dispatchMessage(Message message){
handleMessage(message);
}
//供子类重写
protected void handleMessage(Message message){
}
}
至此,Handler,MQ,Looper,Message已经全部实现,我们现在来跑一下ActivityThread中的test方法main看看实际效果:
非常完美,Handler所在的线程收到了子线程发送的消息!
当然了,这个demo中完全没有考虑线程同步,异常处理等情况。如果你有兴趣可以再去细心研究,好好优化一下!
就这么多啦,不足之处还请指正!