Handler面试全解析+手把手带你写Handler(下)

Handler全解析之源码分析+手把手带你写Handler(上)中我们分析了Handler的源码,了解到Handler, MessageQueue, Looper,ThreadLocal,Message之间的关系,消息是如何取出,如何存入的。为了加深记忆,以及方便吹牛逼,今天给大家演示一波如何手写Handler(当然是简易版),重点还是加强对Handler的理解,话不多说,开始吧。

1.ActivityThread

我们知道主线程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的实现我们一步一步的来。

2.Looper

首先要关心的是Looper类,它要做4件事:

  1. 提供一个公开方法创建Looper并保存在ThreadLocal中
  2. 创建loop()方法遍历MessageQueue并取出消息
  3. 对外公开一个方法获取当前线程的Looper对象
  4. 创建一个MessageQueue

有了思路代码就很好写了,来看看Looper.java的具体实现:

package padphone.myapplication.handler.core;

public class Looper {

	//创建一个ThreadLocal全局遍历,保存Looper
    private static ThreadLocal<Looper> 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);
            }
        }
    }
}

3.Message

我们已经知道的是Message中一定会有一个target,一个obj属性,至于其它的一些属性和方法因为在这里不是必须的先省略吧

public class Message {

    public Handler target;
    public Object obj;

}

4.MessageQueue

MessageQueue至少有两个方法,消息的入队和出队。android源码中使用了epoll模型来解决while(true)循环资源抢占的问题,调用的是底层的native方法,我们没有这个条件,只能用BlockingQueue来替代了。

BlockingQueue有两个特点:

  1. 当BlockingQueue为空, 从队列取数据时会让线程等待状态,直到能取出非空的数据,线程会被唤醒。
  2. 当BlockingQueue是满的,存数据到队列时线程也会进入等待状态,直到有空间,线程才会被唤醒。

由于在队列为空时线程会处于等待状态,所以即便我们用while(true)也不会造成死循环,当然了while(true)已经在Looper中用到,我们在MessageQueue中只需要关心消息入队和出队

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class MessageQueue {

	//使用ArrayBlockingQueue来装载消息体,默认数量为50
    private BlockingQueue<Message> 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;
    }
}

5.Handler

最重要的Handler终于来了,但是依然很简单。我们先回顾一下android源码中Handler做了什么事情

  1. 获取Looper对象并通过Looper获取MessageQueue
  2. 对外公开sendMessage(Message msg)方法发送消息,并调用MQ的enqueueMessage方法将消息体加入到消息队列
  3. 使用dispatchMessage方法对外发送消息

虽然sendMessage的方式多种多样,为了方便我们就只写一个sendMessage(Message msg)。在dispatchMessage中有三种回调方式,我们也只取最简单的一种,直接调用本身的handleMessage(Message msg)方法。下面来看一下具体的代码实现:

扫描二维码关注公众号,回复: 8636390 查看本文章
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){

    }

}

6.运行一波

至此,Handler,MQ,Looper,Message已经全部实现,我们现在来跑一下ActivityThread中的test方法main看看实际效果:

非常完美,Handler所在的线程收到了子线程发送的消息!

当然了,这个demo中完全没有考虑线程同步,异常处理等情况。如果你有兴趣可以再去细心研究,好好优化一下!

就这么多啦,不足之处还请指正!

发布了21 篇原创文章 · 获赞 21 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/u013894711/article/details/103890639