AndroidHandler源码级分析及实现

来源:互联网 发布:高性能mysql epub 编辑:程序博客网 时间:2024/06/07 03:30

AndroidHandler源码级分析及实现

1.android handler 概述

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

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

2.源码详细解析

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

第一步: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 ThreadLocal<Looper>mThreadLocal=new ThreadLocal<Looper>();    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();    }}
测试结果贴图:

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

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

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

测试通过,万事大吉!

4.总结


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

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

原创粉丝点击