Android中的Handler处理机制

知识点:
1、消息机制或者叫handler的功能及原理
2、handler轮询消息的原理
3、Message中的when字段作用
4、MessageQueue的无限循环阻塞时,新消息到来如何被唤醒
5、handler接收到Message后,是如何处理的?
6、ThreadLocal的原理

一、handler的原理

Handler是Android 系统提供给开发人员的一个消息处理类,每一个handler实例发出的消息,最终都要有自己来完成处理。Handler通过send方法,将消息发送到每一个MessageQueue消息队列。MessageQueue本身不具备轮询功能。在每一个Thread中,Handler要想轮询消息队列,必须借助于Looper完成,MessageQueue就是在looper的构造方法中实例化的。Looper借助于loop方法,对消息队列进行轮询,以达到读取消息的目的。在loop方法中调用handler实例的成员方法来处理消息。如果handler处理消息的逻辑,和Looper轮询消息队列的逻辑,分别属于不同的Thread,便可以完成线程切换的目的。

二、Handler轮询消息的原理

loop方法提供了一个无限的for循环,Looper通过循环轮询MessageQueue。MessageQueue并不是一个队列,为了方便插入和删除操作,系统通过链表结构存储message消息。通过阅读源码可以发现,如果消息队列为空,for循环仍在执行调用消息队列的next方法,只是消息为空,这个时候不再调用handler分发消息。

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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);  // msg.target就是handler实例
            } 
               ······
        }
    }

三、Message中的when字段作用。

MessageQueue的next成员方法,同时具备查询和删除消息的功能。Handler轮询消息时,通过调用next方法查询消息队列中是否还有消息。next的方法中的for循环也是无限循环,它是通过比较消息形成时刻和当前轮询时刻形成的时间差来决定是否暂停轮询信息的。when字段就是消息形成的时刻,系统巧妙地使用了系统时钟,前后对比完成消息处理。


阻塞代码逻辑

注意nativePollOnce方法,它的功能就是来完成阻塞的。既然有阻塞就有唤醒功能,Binder.flushPendingCommands()方法就是用来唤醒循环的,下面是该方法的注解:

    /**
     * Flush any Binder commands pending in the current thread to the kernel
     * driver.  This can be
     * useful to call before performing an operation that may block for a long
     * time, to ensure that any pending object references have been released
     * in order to prevent the process from holding on to objects longer than
     * it needs to.
     */
    public static final native void flushPendingCommands();

该方法将暂停在当前线程上的Binder命令刷入kernel内核驱动。在执行任何阻塞操作之前调用这个方法,可以确保所有暂停的对象引用被释放。这样做是为了防止进程在不必要的时间持有当前对象。

那么,when字段有什么用呢?
when字段作用
从源码中可以看到,when是用来和调用消息的系统当前时间对比,以此决定是否暂停轮询,即阻塞线程。

顺便说一句,上面的代码中有如下逻辑:

            if (msg != null && msg.target == null) {
               // Stalled by a barrier.  Find the next asynchronous >message in the queue.
               do {
                   prevMsg = msg;
                  msg = msg.next;
               } while (msg != null && !msg.isAsynchronous());
           }

注意到if执行的条件是msg != null 并且 msg.target也就是handler为null,下面还跟了一行注解,被屏栅拦截,寻找队列中下一个异步消息。换句话说,这个逻辑是为异步消息准备,同步消息被拦截。这就是所谓同步屏障。他能保证异步消息优先执行,因为通常异步消息相对重要一些,比如网络请求后更新UI的操作。

四、MessageQueue的无限循环阻塞时,新消息到来如何被唤醒

如果新消息到来,MessageQueue会调用enqueue方法。该方法最终会唤醒正在暂停的线程指令。代码如下,


enqueue方法唤醒逻辑

其实这里用的是Linux系统的epoll机制。该机制监听一个事件,没有什么事儿的时候就休眠,让出CPU。如果被监听事件触发激活,该机制就会被唤醒抢占CPU资源工作。在Native层提供了三个方法来完成整个一系列流程:

int epoll_create(int size) // 创建句柄
int epoll_ctl() // 添加、删除、修改 监听事件
int epoll_wait() // 进入等待
而该机制的唤醒在Java层是通过nativeWake,在Native层则是通过底部NativeMessageQueue的Looper#wake唤醒的。

五、handler接收到Message后,是如何处理的?

  /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {    //  代码1,当handler调用post(new Runnable)时
            handleCallback(msg);
        } else {
            if (mCallback != null) {   //   代码2,当handler通过new Handler(new Handler.Callback())
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg); //    代码3,使用handler派生类时
        }
    }

handler处理消息的逻辑:

// 代码1,通过POST抛出到主线程
        root.post(Runnable { 
            
        })
// 代码2,通过Callback,比较实用
    Handler handler = new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });
// 代码3,定义派生类
    private static class MyHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    }

    private MyHandler myHandler = new MyHandler();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                         ......
        myHandler.sendEmptyMessage(-1);
    }

处理优先级从上到下。

六、ThreadLocal的原理

对于多线程通信,ThreadLocal声明的对象,在每个线程都是线程私有的,任意赋值操作对其他线程没有影响。


Thread local保存Looper对象

从图中ThreadLocal的使用可以发现,它提供了连个基本的set和get方法:

     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

从代码可以看出,ThreadLocal先通过当前线程t,获取一个ThreadLocalMap的实例,若不存在就创建一个。将当前线程Thread、ThreadLocal、Value关联起来,这样Thread和Value就关联起来了。

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

读取存储value时,仍然是先获取当前线程t,然后通过t去获取Thread的ThreadLocalMap,

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }


    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

在ThreadLocalMap里,当前线程的ThreadLocal是key,所以,需要系统如法炮制,将当前线程的ThreadLocal实例传入,最终拿到了存储在ThreadLocalMap里的value。
在整个存储和获取过程中,Thread扮演着key的角色,ThreadLocal则是中继这个key完成取值操作。

你可能感兴趣的:(Android中的Handler处理机制)