Android Handler 源码解析

Handler主要用于 线程间 的通信,Handler主要是 由MessageQueueMessageLooperHandler ,共同组成,称为Handler消息机制,存储Looper使用了 ThreadLocal ,下面我们一次讲解这几个类

  • Handler 主要负责发送消息,和处理消息
  • MessageQueue 主要负责储存消息
  • Looper 主要负责从 MessageQueue 中取出消息,然后分发给 Handler
  • ThreadLocal 主要负责存储不同线程的 Looper 对象
  • Message 主要负责存储数据

ThreadLocal

ThreadLocal 是一个线程内部的数据储存类,通过他可以在指定线程中储存数据,数据存储后,只有指定线程才可以可以获取储存数据,对于其他线程来说,则无法获取到数据;一般来说当某些数据是以线程为作用域,且不同线程有不同副本的时候,就可以考虑采用 ThreadLocal ,比如对于 Handler 来说,他们需要获取不同线程的 Lopper ,这个时候就需要通过 ThreadLocal 可以轻松在不同线程存储 Looper

ThreadLocal 另一个使用场景是复杂逻辑的对象传递,比如监听器传递,有时候一个线程的任务过于复杂,这可能表现为函数作用栈比较深,以及代码入口的多样性,在这种情况下,我们需要监听器贯穿整个线程,这个时候就可以采用 ThreadLocal ,让监听器作为线程的全局对象而存在,线程内只要get就可以获取监听器

ThreadLocal的使用

mThreadLocal = new ThreadLocal<>();
        mThreadLocal.set(true);
        Log.d("mmm","当前线程"+Thread.currentThread()+"ThreadLocal存储"+ mThreadLocal.get());
        new Thread("thread1"){
            @Override
            public void run() {
                super.run();
                mThreadLocal.set(false);
                Log.d("mmm","当前线程"+Thread.currentThread()+"ThreadLocal存储"+ mThreadLocal.get());
            }
        }.start();
        new Thread("thread2"){
            @Override
            public void run() {
                super.run();
                Log.d("mmm","当前线程"+Thread.currentThread()+"ThreadLocal存储"+ mThreadLocal.get());
            }
        }.start();
复制代码

我在主线程设置了 truethread1 设置了 falsethread2 没有设置,按照正常来说获取,主线程 为 true ,thread1是false, thraed2为 null ,看一下log

09-28 11:30:12.616 32536-32536/com.example.jh.rxhapp D/mmm: 当前线程Thread[main,5,main]ThreadLocal存储true
09-28 11:30:12.618 32536-32745/com.example.jh.rxhapp D/mmm: 当前线程Thread[thread2,5,main]ThreadLocal存储null
09-28 11:30:12.619 32536-32744/com.example.jh.rxhapp D/mmm: 当前线程Thread[thread1,5,main]ThreadLocal存储false
复制代码

ThreadLocal源码

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

set 方法就是通过当前线程获取一个 ThreadLocalMap ,然后通过 ThreadLocalMap 去储存数据,如果 ThreadLocalMapnull 那么久同过当前 thread 去创建一个 ThreadLocalMap ,再去存储,下面我们看一 下ThreadLocalMap 是如何创建的

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    
    class Thread implements Runnable {
   ...
    ThreadLocal.ThreadLocalMap threadLocals = null;
    }
复制代码

每一个 Thread 内部都有一个 ThreadLocalMap 对象,如果这个对象为null,就为他重新赋值,然后我们看他是如何 set 数据的

private void set(ThreadLocal key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
复制代码

首先用 key 计算出数组下标,然后从 Entry[] 中取出值,如果有数据则重新赋值,如果没有数据,则创建一个新的 Entry 添加到 Entry[] 数组中

下面我们看一下get方法

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 getMap(Thread t) {
        return t.threadLocals;
    }
    
    
    private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
    
    
复制代码

首先获取此线程的 ThreadLocalMap ,如果 不为Null ,就用 key 计算出 Entry[] 数组下标,然后取出 Entry ,然后再取出具体的值,如果 ThreadLocalMap为Null 或者取出的 Entry为Null ,就重新赋值

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
复制代码

ThreadLocal总结

每一个线程中都会有一个 ThreadLocal.ThreadLocalMap threadLocals = null; 成员变量,我们操作 ThreadLocal的set个get方法 时,都是操作的单个线程中 ThreadLocalMap 对象,而 ThreadLocalMap 中是以 Entry[] 数组来储存数据,所以就实现了每个线程都会有不同的值

Lopper

创建Lopper

public static void prepare() {
        prepare(true);
    }
    
 private static void prepare(boolean quitAllowed) {
        //一个线程只允许创建一个looper
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
    
  private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
复制代码

利用静态 prepare 方法,来创建 Looper ,对于无参的情况,默认调用 prepare(true) ,表示 Looper 允许退出, false 表示不允许退出,一个线程只允许创建一个 LooperLooper 储存在 ThreadLocal 中,这样就实现了一个线程一个Looper,创建Looper的时候还创建一个 MessageQueue

prepareMainLooper

该方法主要在ActiityThread只使用,创建主线程的Looper

public static void prepareMainLooper() {
        //该Looper不允许退出
        prepare(false);
        synchronized (Looper.class) {
            //把该Looper设置为主线程Looper,只能设置一次
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
 //获取主线程的Looper    
 public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }
复制代码

loop()

public static void loop() {
        //获取本线程的looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获取MessageQueue
        final MessageQueue queue = me.mQueue;
        ...

        for (;;) {
            //从MessageQueue中取出消息,没有消息就会阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                // 一般情况msg不会为null,只有messageQueue退出,msg才会返回null
                return;
            }
            ...
                //msg.target其实就是Handler对象,把消息分发给Handler
                msg.target.dispatchMessage(msg);
                ...
                //把Message放入消息池
                msg.recycleUnchecked();

        }
    }
复制代码

loop() 方法进入无限循环,不断重复以下操作

  • MessageQueue 中取出 Message
  • Message 分发给对应的 Handler
  • 把分发后的 Message 回收到消息池,以便重新利用

quit()

public void quit() {
        //移除消息
        mQueue.quit(false);
    }

  public void quitSafely() {
        //安全的移除消息
        mQueue.quit(true);
    }
复制代码

Looper.quit() ,最终调用的是 MessageQueuequit 方法,这俩个方法区别就是,一个 quit 方法会,直接退出, quitSafely 会执行完剩余的消息退出

Looper总结

Looper 的主要工作是,从 MessageQueue 中获取消息,然后分发给对应的 Handler ,除了主线程,其他线程都需要自己去调用 Looper.prepare() 方法创建 Looper ,因为主线程的 LooperActivityThreadmain 方法里面创建了,创建完成之后在调用 Looper.loop 方法进行循环,下面是一个创建 Looper 的经典例子

class LooperThread extends Thread {
        public Handler mHandler;
  
        public void run() {
            Looper.prepare();
  
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
  
            Looper.loop();
        }
    }
复制代码

Handler

构造方法

public Handler() {
        this(null, false);
    }
    
  public Handler(Callback callback) {
        this(callback, false);
    }
    
 public Handler(boolean async) {
        this(null, async);
    }
    
  public Handler(Callback callback, boolean async) {
    ...
        //获取此线程中的looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //获取looper中的MessageQueue
        mQueue = mLooper.mQueue;
        //是否设置了Callback
        mCallback = callback;
        //是否为异步
        mAsynchronous = async;
    }
复制代码

这个几个构造方法,最终都调用了俩个参数的构造方法,对于无参的构造方法,默认使用本当前线程中的 loopercallbacknull ,消息为同步处理的方式

public Handler(Looper looper) {
        this(looper, null, false);
    }

  public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

  public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
复制代码

Looper 为参数的构造方法,可以指定 Looper

发送消息

这是发送消息的调用链,我们发现最终都是调用了 MessageQueue.enqueueMessage()

send

public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

  public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

  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赋值
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
复制代码

post

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
复制代码

Handler.sendEmptyMessage() 系列方法,最终调用了 MessageQueue.enqueueMessage(msg, uptimeMillis) ,将消息添加到消息队列中,其中 uptimeMillis 是系统时间加上延迟时间

分发消息

在Looper.loop()方法中,发现有消息,会调用msg.target.dispatchMessage方法,来分发消息

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
private static void handleCallback(Message message) {
        message.callback.run();
    }
复制代码

分发流程

  • msg 中有 callback 时,则调用 message.callback.run() ;方法,其中的 callback 指的 Runnable
  • 如果 callback 为空,那么则看一下成员变量的 mCallback 是否为空,这个是 Handler 的构造方法传入的
  • 如果 mCallback 也为空,则调用 handleMessage 方法,这个一般在 Handler 的子类中重写

其他方法

removeMessages

移除消息,其实还是操作的MessageQueue,下面再一起分析

public final void removeMessages(int what, Object object) {
        mQueue.removeMessages(this, what, object);
    }
复制代码

Handler总结

Handler主要工作就是,发送消息,最终是把消息插入到了MessageQueue中,然后通过Looper.loop方法,循环从MessageQueue拿出消息,然后通过Handler把消息分发出去,这就完成了一次循环

MessageQueue

MessageQueue是java层和c++层链接的纽带,大部分的核心方法都是交给native层去做,MessageQueue中的native方法如下

private native static long nativeInit();
    private native static void nativeDestroy(long ptr);
    private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
    private native static void nativeWake(long ptr);
    private native static boolean nativeIsPolling(long ptr);
    private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
复制代码

想要详细了解这些native方法做了什么情移步到 gityuan 大神的博客 Android消息机制2-Handler(Native层)

创建MessageQueue

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        //通过native代码初始化消息队列
        mPtr = nativeInit();
    }
复制代码

enqueueMessage 插入消息

boolean enqueueMessage(Message msg, long when) {
        //msg.target不能为空
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            ...
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // 如果p==null表示消息队列为空,或者msg消息触发时间为队列最早,则把消息插入头部,如果阻塞唤醒队列
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //按照时间顺序插入到队列中,不需要唤醒队列
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
复制代码

MessageQueue的插入,其实就是链表的插入,是按照Message的触发时间先后顺序排列的,消息头是最早触发的,当有消息假如队列时,会从头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序

next 获取消息

Message next() {
        final long ptr = mPtr;
        //如果消息循环已经退出就直接返回null
        if (ptr == 0) {
            return null;
        }
        // 注意这里首次循环为-1 
        int pendingIdleHandlerCount = -1; 
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //阻塞操作,等待nextPollTimeoutMillis时长,或者被唤醒都会返回
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                ...
                if (msg != null) {
                    if (now < msg.when) {
                       //当前时间小于下个消息测触发时间,就重新设置阻塞的时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //如果消息队列不为空,并且当前时间大于等于消息的触发时间,直接把消息返回,然后从消息队列移除此消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    //没有消息则把nextPollTimeoutMillis设置为-1
                    nextPollTimeoutMillis = -1;
                }

                // 如果消息正在推出则返回null
                if (mQuitting) {
                    dispose();
                    return null;
                }

                //这里是idlehandler,注意这里pendingIdleHandlerCount < 0才会进入,而等于0不会进入,什么时候小于0呢,其实就是第一次进入循环,赋值为-1
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //注意这里pendingIdleHandlerCount <= 0,小于等于0就直接continue,不会走下面的代码
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            //上方是源码注释,意思是,运行IdleHandler,但是只会在第一次迭代运行
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            //上边是源码的注释,意思就是把它重新赋值为0,也就意味着IdleHandler只执行一次
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }
复制代码
  • 首先进入先判断是否已经退出,退出直接返回,不退出进行下一步

  • 之后再判断当前的 MessageQueue 是否为空,为空则赋值阻塞时间 nextPollTimeoutMillis = -1;

  • 如果不为空,则判断当前时间是否大于等于消息的触发时间,如果小于触发时间,则赋值阻塞时间 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

  • 如果当前时间大于等于触发时间,则直接取出消息返回,并且把此消息移除队列

  • 其中涉及一个方法 nativePollOnce(ptr, nextPollTimeoutMillis); 这是一个native方法,主要作用是阻塞, nextPollTimeoutMillis 代表阻塞时间

    • 其中 nextPollTimeoutMillis=-1 表示,一直阻塞,直到被唤醒
    • 其中 nextPollTimeoutMillis=0 表示,不阻塞,立即返回
    • 其中 nextPollTimeoutMillis>0 表示,阻塞 nextPollTimeoutMillis 毫秒,如果期间唤醒也会立即返回

IdleHandler

上方的源码继续向下分析就是 IdleHandler ,我之前写的一篇文章 Android LeakCanary的使用和原理 ,LeakCanary中使用了IdleHandler

void waitForIdle(final Retryable retryable, final int failedAttempts) {
    // This needs to be called from the main thread.
    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
      @Override public boolean queueIdle() {
        postToBackgroundWithDelay(retryable, failedAttempts);
        return false;
      }
    });
  }
复制代码

IdleHandler的作用是在当前线程消息队列空闲时,去做一些我们想要做的操作,但是IdleHandler只会执行一次,上面注释已经描述的很清楚了

Message

Message主要包括以下信息

数据类型 成员变量 解释
int what 消息类别
long when 消息触发时间
int arg1 参数1
int arg2 参数2
Object obj 消息内容
Handler target 消息响应方
Runnable callback 回调方法

消息池

Message 维护了一个消息池, recycle() 方法可以把用过的消息假如到消息池中,这样做的好处是,当消息池不为空时,可以直接从中取出 Message 使用,而不是重新创建,提高效率

静态变量 sPool 的数据类型是 Message ,其实是一个链表,维护这个消息池, MAX_POOL_SIZE 代表容量,默认50

recycle()

public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }
    
   void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
复制代码

其实就是一个链表的插入,把信息清除,然后插入

obtain() 从消息池中获取消息

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
复制代码

如果sPool不为null,就从池子了取出一个Message,如果为null,就直接New一个返回

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章