美文网首页
Handler探究

Handler探究

作者: Jansid | 来源:发表于2017-06-10 18:21 被阅读8次

    在项目中经常用Handler,然而一直没有深究其原理,今天就来探究一下这个熟悉的陌生人。
    提到Handler就需要提到LooperMessage了,这三者就像刘关张一样,是拜把子的兄弟。
    先介绍一下他们三个:
    Handler: 句柄,负责向MesageQueue消息队列中发消息和处理消息
    Looper: 循环器,不断从MessageQueue消息队列中取消息
    Message: 消息

    接下来看看Handler的原理,我们知道Android的应用是由事件驱动运行的。Java程序的入口函数是main()函数,Android应用也是一样,Android应用启动也是从main()函数开始,这个函数在ActivityThread中。
    我们来看看main()方法的内容:

            ...上面省略
            Looper.prepareMainLooper();
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
    
            // End of event ActivityThreadMain.
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            Looper.loop();
            throw new RuntimeException("Main thread loop unexpectedly exited");
    

    看到有一行是Looper.prepareMainLooper()从命名看出它的作用因该是准备一个主Looper,我们来看看prepareMainLooper()的源码:

    public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    

    从名字可以看出,这个方法的作用是用于准备主Looper

    接下来看prepare(false)的源码:

    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));
        }
    

    形参看名字就知道是,是否允许退出,很明显这个不允许的。
    这里有个sThreadLocal 变量,我们来看看它是干什么用的

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    

    在成员变量中找到了它的定义,发现这是ThreadLocal类型的变量,那么,ThreadLocal是干什么的呢?
    看到它的get() set()方法,感觉它应该是一个容器。接下来我们来验证一下,先看看set()方法:

    public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
    }
    

    这里有一个ThreadLocalMap 我们查看这个类的注释发现有这么一句:
    ThreadLocalMap is a customized hash map suitable only for maintaining thread local values.
    说明这是一个自定义的HashMap容器(改进了HashMap的算法,以使得排列更加均匀)
    那么就可以很清晰看到这里是将ThreadLocal作为键,Looper作为值存储起来。从而得出结论,ThreadLocal的作用就是将Looper与当前线程(主线程)关联起来,并且保证Looper的唯一(线程安全)。
    这样looper()方法就看完了,塔的作用就是创建一个Looper并将其与主线程关联。
    接下来看看最后一行的Looper.loop()(抛异常的暂时忽略),由于代码较多,只看主要的

    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;
            ···
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
            ···
                try {
                    msg.target.dispatchMessage(msg);
                } finally {
                    ···
                }
            ···
                msg.recycleUnchecked();
            }
        }
    

    可以看到,这里先是获取了一个Looper对象,这里的myLooper()的代码很简单,就是调用了ThreadLocalget()方法,获取Looper对象,如果这里报了异常,也就是说之前没有调用Looper.prepare()方法。这里就不多说了
    接下来就是获取消息队列。
    然后就是一个死循环,遍历消息队列中的消息。queue.next()也就是一直获取下一个消息。如果消息队列中没有消息则退出。
    接下来看msg.target.dispatchMessage(msg);,看到后面的dispatchMessage(msg)发现这个方法有点熟悉,这不是Handler里面的方法么? 我们来看看Messagetarget究竟是什么👻?
    我们在属性中看到了它: Handler target;,这就很清晰了。
    接下来我们看看Handler的dispatchMessage(msg)方法

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
             if (mCallback != null) {   
                 if (mCallback.handleMessage(msg)) {
                     return;
                 }
             }
             handleMessage(msg);
        } 
    }
    

    首先看这个方法的名字,可以知道它的作用是分发消息
    先看msg.callback,这个callback又是什么呢?通过查看Message的属性,看到Runnable callback;,那么msg.callback就是一个Runnable类型的变量了。
    接下来看handleCallback(msg);

    private static void handleCallback(Message message) {
        message.callback.run();
    }
    

    发现这里会调用Runnablerun()方法。这个时候大脑灵光一闪,我们平常通过Handler发消息时使用的handler.post(Runnable)最终是不是通过这里回调的run()方法呢?答案是肯定的,后面再分析
    继续看,mCallback是一个接口,里面有一个抽象方法,如果我们在创建Handler的时候实现了这个接口,就会在这里回调了。
    比如:

    Handler dHandler = new Handler(new Handler.Callback() {
        @Override public boolean handleMessage(Message msg) {
            return false;
        }
    });
    

    继续看,如果handleMessage(msg);,这里就不用解释了。
    举例:

    Handler dHandler = new Handler() {
        @Override public void handleMessage(Message msg) {
          
        }
    };
    

    这样我们就能理解loop()方法了,就是不断的循环,从消息队列中取出消息,通过回调让Handler处理消息。
    loop()方法最后一行msg.recycleUnchecked();这个的意思就是回收消息。
    前面说了,Android应用是由事件驱动的,应用在运行时不断的从消息队列中拿消息处理消息,那么我们如果每发一个消息就new一个对象显然是不行的,因此,在MessageQueue中有一个消息池,消息是用链表方式存放的,消息池中的消息不断被使用-回收。

    再来看看我们平常使用Handler发消息的用法:

    Handler handler = new Handler();
    Runnable run = new Runnable() {
        @Override public void run() {
        }
    };
    handler.post(run);
    

    通过查找post()方法的源码,发现里面调用的是sendMessageDelayed(getPostMessage(r), 0);方法,我们来看看它的源码:

    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    

    发现这里面调用了另一个方法,继续追溯到sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);方法:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    

    我们来看看enqueueMessage(queue, msg, uptimeMillis)方法

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

    这里可以看到最后是插入消息,将Handler发的消息插入到消息队列中并且会将发消息的Handler绑定到Message中。在将消息插入消息队列时会附加一个时间,而消息队列里面的消息就是按这个时间来排序。前面说了,应用启动了之后会不断从消息队列中取消息,而我们用Handler发送消息的时候,消息会被插入到消息队列中,然后Looper一直从消息队列中取消息,并将Message通过其绑定的Handler回调处理。

    Handler事件发送和处理

    相关文章

      网友评论

          本文标题:Handler探究

          本文链接:https://www.haomeiwen.com/subject/lhnqqxtx.html