Handler机制实现原理

2018-08-19  本文已影响0人  文子产品笔记

面试的时候经常会问handler原理啥的,前段时间刚好看了一个老师讲handle机制,老师讲得很仔细清晰,这里我自己也用代码模拟安卓handler实现一个基本线程通信。
说到handler就不得不说消息处理的五大组成部分:Message,Handler,Message Queue,Looper和ThreadLocal。首先简要的了解这些对象的概念;

Message:message就是一个数据模型吧,它的作用仅限于线程之间通信的时候传递消息,他可以携带少量数据,用于线程之间传递信息,常用的四个字段target,what,obj,arg;

target:消息回调后的作用域类,通常是一个handler。
what:是一个区分不同消息的标识符。
obj:这是obj是一个对象类型,可以携带自定义的类。
arg:int类型,携带的参数。
下面贴出Message代码:

public class Message {
    Handler target;
    public int what;
    public Object obj;

    @Override
    public String toString() {
        return obj.toString();
    }
}
handler:它主要用于发送和接收消息,有三个主要方法,这里实现基本功能,不探讨具体细节

sendMessage();
dispatchMessage();
handleMessage();

它主要用于发送和处理消息的发送消息一般使用sendMessage()方法,还有其他的一系列sendXXX的方法,但最终都是调用了sendMessageAtTime方法,把消息压入消息队列中。
而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage方法中。这里我们handleMessage()方法在外部重写,内部实现调用。

public class Handler {

    private MessageQueue mQueue;
    private Looper mLooper;

    // Handler的初始化在主线程中完成
    public Handler(){
        //获取主线程的looper对象
        mLooper = Looper.myLooper();
        this.mQueue = mLooper.mQueue;
    }

    // 发送消息,压入队列
    public void sendMessage(Message msg){
        msg.target = this;
        mQueue.enqueueMessage(msg);
    }

    //内部调用,外部实现
    public void handleMessage(Message msg){

    }

    // 转发
    public void dispatchMessage(Message msg){
        handleMessage(msg);
    }
}
MessageQueue是消息队列,主要存放所有handler发送过来的消息,这些消息会一直存放消息队列中,等待被处理,每一个线程只有一直MessageQueue队列。

这里实现的消息队列里面有一个入栈和出栈函数,这两个函数的关系是一个生产者和消费者的关系,我们在Looper.loop方法中通过while死循环方法不断检测生产者方法,一旦消息队列不为空,立即取出消息并处理,同时消息的总数量也相应需要改变。

public class MessageQueue {

    //通过数组的结构存储message对象
    Message[] items;

    //入队和出队元素索引位置
    int putIndex;
    int takeIndex;

    // 计数器
    int count;


    // synchronized (msg){} 代码块加锁
    // 互斥锁
    Lock lock;
    // 条件变量
    Condition notEmpty;
    Condition notFull;

    public MessageQueue(){
        this.items = new Message[50];

        this.lock = new ReentrantLock();   //这个锁和sychronize还是有些区别的,ReentrantLock 类实现了 Lock ,它拥有与synchronized 相同的并发性和内存语义,但是添加了类似轮询锁、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)
        this.notEmpty = lock.newCondition();
        this.notFull = lock.newCondition();
    }

    // 加入队列
    // 生产
    public void enqueueMessage(Message msg){
        System.out.println("加入队列");

        try {
            lock.lock();

            //消息队列满了,子线程停止发送消息,阻塞
            while (count == items.length){
                try {
                    notFull.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            items[putIndex] = msg;
            //循环取值
            putIndex = (++ putIndex == items.length)?0:putIndex;
            count ++;

            //生产出来新的message对象,通知主线程
            notEmpty.signalAll();

        }finally {
            lock.unlock();
        }
    }

    // 出队列
    // 消费
    public Message next(){

        //消息队列空了,子线程停止发送消息,阻塞
        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 --;

            //使用列一个message对象,通知子线程,可以继续生产
            notFull.signalAll();

        }finally {
            lock.unlock();
        }

        return msg;
    }
}
Looper:每个线程通过Handler发送的消息都保存在,MessageQueue中,Looper通过调用loop()的方法,就会进入到一个无限循环当中,然后每当发现Message Queue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。

/**
 *  一个线程对应一个looper对象,一个looper对应一个消息队列
 */

public final class Looper {

    //每一个主线程都有一个looper对象
    //Looper 对象保存在threadlocal中,保证列线程数据的隔离
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    // 一个looper对象 对应一个消息队列
    MessageQueue mQueue;

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

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

    //获取当前线程的looper对象
    public static Looper myLooper(){
        return sThreadLocal.get();
    }

    //轮询消息队列
    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;
            }
            // 转发给handler
            msg.target.dispatchMessage(msg);
        }
    }
}
在Main方法中写个测试例子
    public void test(){
        //轮询器初始化
        Looper.prepare();

        // 主线程当中
        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();
    }

打印结果如下:

// 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: 加入队列
// 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: 加入队列
// 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: 加入队列
// 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: 加入队列
// 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: 加入队列
// 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

结果分析可以看到:只要子线程中一加入消息,那么looper就会轮询从massagequeue中取出消息并且通过dispatchMessage发送到主线程中取执行。我个人觉得所谓主线程,只不过比子线程中多了一个looper,我们的UI线程在只能在主线中刷新,就是应为线程的loop方法不断轮询绘制的原因,子线程之所有不能刷新UI,是因为子线程没有loop方法,如果我们把子线程中设置一个looper,那么子线程也是可以刷新绘制UI的。
以上是我个人见解,欢迎大家斧正。

上一篇下一篇

猜你喜欢

热点阅读