源码分析Handler机制

原创
2019/07/09 17:00
阅读数 47

Android的消息机制主要是指Handler的运行机制,再加上很多公司面试直接问Handler的机制,所以本篇文章就用"Handler的机制"代替"消息机制"。。 

  1. Android的消息机制概述
  2. ThreadLocal的工作原理
  3. Looer的工作原理
  4. Handler的工作原理
  5. 消息队列的工作原理
  6. 主线程的消息循环

Handler的运行需要底层的MessageQueue和Looper的支撑。

1.Android的消息机制概述

开发角度来说,Handler是Android消息机制的上层接口,这使得在开发过程中只需要和Handler交互即可。

Handler的使用过程很简单,通过它可以轻松地把一个任务切换到Handler所在的线程去执行。

常用使用场景:在子线程中做耗时操作(如网络请求数据、读取文件或读取数据库数据等),操作完成需要更新UI,这个时候使用主线程的Handler很容易的将更新UI操作切换到主线程执行。

 

2.ThreadLocal的工作原理

Handler机制中源码不太好理解的代码ThreadLocal<T>类

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储后只有在指定的线程中才可以获取到存储的数据,对于其他线程则无法获取到数据。

 ThreadLocal类中set方法来保存线程数据,保存数据交由ThreadLocalMap对象实现。

    //保存数据到ThreadLocalMap中
    public void set(T value) {
        Thread t = Thread.currentThread();
        //获取线程中的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else{
            createMap(t, value);
        }
    }

ThreadLocalMap类是ThreadLocal类中定义的一个静态内部类。

线程首次调用ThreadLocal对象的set方法map为null,会调用createMap()方法来初始化ThreadLocalMap对象。保存线程对象到Entry类型的数组中。

       //初始化过程,保存线程数据到Entry对象中
       ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            //默认创建数组大小为16
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

Entry类是ThreadLocalMap类中定义的一个静态内部类,找到数据保存源头了,Entry类才是最终保存线程数据的。

        //登记类,用来存储TreadLocal对象与数据T
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

ThreadLocal类的代码

    //保存数据到ThreadLocalMap中
    public void set(T value) {
        Thread t = Thread.currentThread();
        //获取线程中的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            map.set(this, value);
        } else{
            createMap(t, value);
        }
    }

    //从ThreadLocalMap中获取数据
    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();
    }

    //获取线程中的ThreadLocalMap对象
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    //实例化线程中的ThreadLocalMap对象
    void createMap(Thread t, T firstValue) {
        //第一个参数把当前ThreadLocal对象作为参数传递给ThreadLocalMap构造方法
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    static class ThreadLocalMap {
        //存储线程数据的数组
        private Entry[] table; 

       //初始化过程的同时保存线程数据到table数组中
       ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            //默认创建数组大小为16
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
       

        //登记类,用来存储TreadLocal对象与数据T
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        private void set(ThreadLocal<?> key, Object value) {
            //创建Entry数组
            Entry[] tab = table;
            //把ThreadLocal对象与数据保存到数组tab中
            tab[i] = new Entry(key, value);
            //如果数组中已经存在了ThreadLocal对象的数据,那就修改数据值
            e.value = value;
        }
    }


线程Thread类中定义了ThreadLocalMap类型变量threadLocals。

    //声明一个ThreadLocalMap类型变量
    //具体初始化在ThreadLocal类中的createMap()
    //默认threadLocals对象可以存储线程的16个对象数据
    ThreadLocal.ThreadLocalMap threadLocals = null;

    

Android利用该特性把Looper对象存储到ThreadLocal中,这样保证了只有在当前线程中才能获取到与当前线程关联的Looer对象。

3.Looper的工作原理

Looper类中定义了全局常量ThreadLocal<Looper>类型mThreadLocal。

static final ThreadLocal<Looper> mThreadLocal = new ThreadLocal<Looper>();

初始化Handler前需要调用Looler.prepare()方法--只能调用一次,否则抛出异常。把Looper实例对象保存到mThreadLocal实例中。

    public static void prepare() {
        prepare(true);
    }

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

接下来我们查看Looper的初始化过程,创建了消息队列实例mQueue。


    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

这样初始化Handler的准备工作已经完成。

初始化Handler对象

public Handler mHandler;
public void run() {
    Looper.prepare();
    mHandler = new Handler();
    //接下来的工作
    
}

这样Handler还不能工作,就算你向消息队列中插入了消息,Handler也不会处理的。

需要开启循环命令Looer.loop()。这样Handler就开始工作了。

进入loop方法内部一探究竟,都做了什么工作。

        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }

这就是经常被面试官问到的问题“子线程中Handler能直接初始化么?”

获取线程中的对象数据Looper

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

下一行代码,获取消息队列

final MessageQueue queue = me.mQueue;

再接着看有一个无限循环for(;;),如果从消息队列中获取到的消息为null则结束循环,loop方法返回。

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

从消息队列中获取到消息对象,调用消息对象中的targer对象的dispatchMessage()方法。

msg.target.dispatchMessage(msg);

Message类中的target对象就是Handler类型。

Handler target;

4.Handler的工作原理

Handler发送消息方法sendMessage(),通过一系列的方法调用最终调用enqueueMessage(),在该方法中给target赋的值msg.target = this;把当前Handler对象赋值给target变量。

    public final boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return 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);
    }

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

    

至此兜兜转转又回到了Handler类中,调用了Handler中的dispatchMessage()方法。

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

该方法中分了三种情况

1 msg的callback对象不为空,调用callback对象的run方法。

Message类中的属性 
    Runnable callback;

Handler类中的成员方法

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

2 mHandler对象中的mCallback对象不为空,调用mCallback的handlerMessage()方法。

    final Callback mCallback;

    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }

3 就是我们常用的,调用我们重写的handlerMessage()方法。该方法默认实现为空。

    public void handleMessage(Message msg) {
    }

5.消息队列的工作原理

Looper循环是调用消息队列的next()方法来获取消息的。

Handler中调用消息队列的enqueueMessage()方法向消息队列中插入消息的。

可知MessageQueue主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作

尽管MessageQueue叫消息队列,但是它的内部实现并不是用的队列,实际上它是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。

 插入消息方法enqueueMessage

boolean enqueueMessage(Message msg, long when) {
    ...
    //多线程需要排队插入消息
    synchronized(this) {
        ...
        msg.markInUse();
        msg.when = when;
        //当前链表的表头元素
        Message p = mMessages;
        boolean needWake;
        //如果当前链表为空
        //当前消息时需要插入链表的表头
        if (p == null || when = 0 || when < p.when) {
            msg.next = p;
            mMessage = msg;
            needWake = mBlocked;
        } else {
            Message pre;
            for(;;) {
                prev = p;
                p = p.next;
                //已经到了链表表尾
                //或消息已经插入到了合适的位置
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous) {
                    needWake  = false;
                }
            }
            msg.next = p;
            pre.next = msg;
            
        }
        if (needWake) {
            //唤醒阻塞方法next
            nativeWake(mPtr);
        }
        
    }
    return true;
}

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部