AndroidHandler源码级分析及实现

AndroidHandler源码级分析及实现

1.android handler 概述

百度百科:Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。

handler,looper,message三者都是与android异步消息处理机制相关的概念。当调用handler.sendMessage()时,会调用queue.enqueueMessage()消息入队。然后出队时Looper消息轮循器会调用looper.loop(),然后MessageQueue.next(),再然后在回调message.dispatherMessage(),最后再调handlerMessage(),就完成了出队。

2.源码详细解析

先上一张图,有助于大家理解,下面我会刨析这张图:

AndroidHandler源码级分析及实现_第1张图片

第一步:handler.sendMessage()发送消息


不管你是调用哪个发送消息的方法,最终都会调用下面的方法,看到了return语句没有,是不是调用了queue.enqueueMessage(),这样就完成了消息入队,至于queue是什么,其实queue就是MessageQueue,可以理解为消息的仓库,简称消息队列。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

第二步:Lopper轮询器会轮询消息


private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
ThreadLocal防止多线程脏读问题
 public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block//消息出队
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);//消息转发再由handlerMessage()处理
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }
loop->MessageQueue.next()->message.dispatherMessage()
 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
可以看出message.dispatherMessage()->handlerMessage()


handler大致结构:

Handler类有:Looper looper;MessageQueue mQueue;

Looper类有:MessageQueue mQueue;prepare()->sThreadLocal.set();loop()->myLooper()->sThreadLocal.get()

MessageQueue类有:enqueueMessage()消息入队/生产者;next()消息出队/消费者

Message类有:Handler target;int what;Object obj;

下面将根据这个类结构去自定义handler

3.自定义handler

Handler:

public class Handler {
    private Looper mLooper;
    private MessageQueue mQueue;
    public Handler(){
        mLooper=Looper.myLooper();
        mQueue=mLooper.mQueue;
    }

    /**
     * 发送消息
     * @param msg
     */
    public void sendMessage(Message msg){
        msg.target=this;
        mQueue.enqueueMessage(msg);
    }

    /**
     * 转发消息
     * @param msg
     */
    public void dispatherMessage(Message msg){
        handlerMessage(msg);
    }

    /**
     * 处理消息
     * @param msg
     */
    public void handlerMessage(Message msg){

    }
}

Looper轮询器

/**
 * 一个线程只有一个lopper对象,prepare不为空抛异常
 */
public class Looper {
    //ThreadLocal 防止脏读
    private static ThreadLocalmThreadLocal=new ThreadLocal();
    public MessageQueue mQueue;
    public Looper(){
        this.mQueue=new MessageQueue();
    }
    /**
     *
     * @return 返回当前线程绑定的lopper对象
     */
    public static Looper myLooper(){
        return mThreadLocal.get();
    }

    public static void prepare(){
        Looper lopper=myLooper();
        if(lopper!=null){
            throw new RuntimeException("only one looper may be created per thread");
        }
        mThreadLocal.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 mQueue=me.mQueue;
        for(;;){
            Message msg=mQueue.next();
            if(msg==null){
                continue;
            }
            //转发
            msg.target.dispatherMessage(msg);
        }
    }
}

消息队列MessageQueue,对于这个类我多做一点解释,防止不了解线程间通信的读者看不懂。

Object类有三个方法值得注意:wait(),notify(),notifyAll().这三个方法必须有同步监视器对象来调用

wait():导致当前线程等待,直到其它线程调用该同步监视器的notify()或者notifyAll()来唤醒该线程。

notify():唤醒在此同步监视器等待的单个线程,选择是任意性的。

notifyAll():唤醒在此同步监视器等待的所有线程。

传统的线程通信使用Synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监视器,所以可以在同步方法中调用这三个方法。

传统的线程通信使用Synchronized修饰的同步代码块,同步监视器是Synchronized后括号里的对象,所以必须使用该对象调用这三个方法。

这是传统的使用Synchronized关键字来保证同步,如果不喜欢用这个,可以试试Lock对象,本程序用的就是这个对象。如果用的是Lock对象,程序中就不存在隐式的同步监视器,也就不能使用wait(),notify(),notifyAll()。所以java就提供了Conditionl类来保持协调。提供的三个方法await(),signal(),signalAll()与上面三个方法类似,不多累赘。

/**
 * //不加锁,供不应求 ,供大于求
 * Created by Administrator on 2017/5/29.
 */

public class MessageQueue {

    Message[]items;//消息数组
    int putIndex;//入队索引
    int takeIndex;//出队索引
    int count=0;//消息个数
    Lock lock;//添加锁 阻塞队列 java高并发编程
    Condition notEmpty;//消费者
    Condition notFull;//生产者
    public MessageQueue(){
        this.items=new Message[50];
        this.lock=new ReentrantLock();
        this.notEmpty=lock.newCondition();
        this.notFull=lock.newCondition();
    }
    /**
     * 消息入队(生产者) sendMessage()里面调用是子线程
     * @param msg
     */
    public void enqueueMessage(Message msg){
        try {
            lock.lock();//加锁
            if (count == items.length) {
                //阻塞 已经满了,不能再生产了
                notFull.await();
            }
            items[putIndex] = msg;
            putIndex = (++putIndex == items.length) ? 0 : putIndex;
           //队列不为空,通知消费者线程可以消费
            notEmpty.signalAll();//防止多个子线程 没有唤醒
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

        count++;
    }

    /**
     * 消息出队(消费者)  looper调用 ,是主线程
     * @r  */
    public Message next(){
        Message msg=null;
        try {
            lock.lock();
            //没有产品了,不能消费
            while(count==0){
                notEmpty.await();
            }
            msg = items[takeIndex];
            items[takeIndex] = null;//出队后置空
            takeIndex = (++takeIndex == items.length) ? 0 : takeIndex;
            count--;
            notFull.signalAll();//防止多个子线程 没有唤醒
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        return msg;
    }
}
Message

public class Message {
    Handler target;//默认修饰符
    public int what;
    public Object obj;

    @Override
    public String toString() {
        return obj.toString();
    }
}
TestHandler测试类(消息机制不光光是更新ui,更准确的说是通信,所以下面的测试代码我故意放在了子线程。for循环开启10个子线程测试我写的handler是否是线程安全的,测试结果显示是线程安全的,因为消息队列加锁了)

public class TestHandler {
    public static void main(String[]args){
        //消息机制不光光是更新ui的,最准确地来说是通信的,因为我可以放在ui线程执行也可以放在子线程
        new Thread(){
            @Override
            public void run() {
                Looper.prepare();
                final Handler handler=new Handler(){
                    @Override
                    public void handlerMessage(Message msg) {
                        System.out.println(Thread.currentThread().getName() + "    receiver:"+msg.toString());
                    }
                };
                for(int i=0;i<10;i++) {//多线程跑测试自己的handler是不是线程安全的,结果发现是的
                    new Thread() {
                        @Override
                        public void run() {
                            while (true) {
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                Message msg = new Message();
                                msg.what = 1;
                                //多线程一起跑randomUUID要加个锁
                                synchronized (UUID.class){
                                    msg.obj = UUID.randomUUID().toString();
                                }

                                System.out.println(Thread.currentThread().getName() + "    send:" + msg.toString() + "    ");
                                handler.sendMessage(msg);
                            }
                        }
                    }.start();
                }
                Looper.loop();
            }
        }.start();


    }
}
测试结果贴图:

AndroidHandler源码级分析及实现_第2张图片

Thread-0就是接受的子线程,就是消费者

Thread-1到Thread-10就是10个子线程,不停地发消息,10个生产者

从上面你可以看出1-10生产者生产出来的msg在0这个消费者总是能接受到消息

测试通过,万事大吉!

4.总结


学习是一个漫长的过程,彼此共勉!加油!!!

ps:上述如有不对的地方,欢迎指出,一起交流!

你可能感兴趣的:(android开发,架构设计)