Android Handler源码阅读(技术记录/回忆)

Handler是Android中最常用线程通讯方式之一、也是非UI线程与线程通讯的主要方式。

你可能有个疑问基础api中AsyncTask、runOnUiThread()还是第三方的RxJava、Eventbus内部都是直接或间接使用Handler实现对UI线程进行更新(参照源码)。

//-- runOnUiThread
public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
//-- Eventbus
package org.greenrobot.eventbus;
import android.os.Handler;
public class HandlerPoster extends Handler 
// Rxjava
Flowable.create(..., BUFFER).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());

那么既然Handler这么牛逼那么它内部是怎么实现更新UI的,以及为什么它能够更新UI线程呢?我相信做Android的或多或少的看过相关的资料,了解Handler的核心机制由Looper、Handler、MessageQueue、Message四大金刚完成;
1.Looper发动机负责转动整个消息队列
2.Handler执行者取队列任务干活的工具人
3.MessageQueue任务链表队列、一个挨着一个的那种
4.Message任务详细信息,以及任务所需要的数据(序列化Parcelable)
那么我们现在一个一个来解读(没有比代码更让我们程序员感到亲切的了,以下...为忽略非关键代码)源码阅读

1.Looper

    // sThreadLocal.get() will return null unless you've called prepare().
    // 每个线程里有且只可能有一个Looper、而所有的线程对应的Looper都存在这里面
    static final ThreadLocal sThreadLocal = new ThreadLocal();
    // UI线程的主Looper,在ActivityThread中的main函数启动的时候创建
    private static Looper sMainLooper;  // guarded by Looper.class
    private static Observer sObserver;

    // 这么快就出现了伙计,当前Looper的消息队列。
    final MessageQueue mQueue;
    // 当前Looper所在的线程
    final Thread mThread;
    ...
    private Looper(boolean quitAllowed) {
        // 参数为是否可安全退出(是否执行完任务再退出)
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    ...
    private static void prepare(boolean quitAllowed) {
        // 一个线程里面只能存一个Looper,所以多次prepare会Exception
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    // 初始化主线程的Looper,不需要我们调用
    public static void prepareMainLooper() {
        ...
    }
    
    // 永动机启动
    public static void loop() {
        final Looper me = myLooper();
        ...
        Binder.clearCallingIdentity();//(native方法切换进程身份pid/uid这里不详细讲解了)
        final long ident = Binder.clearCallingIdentity(); // 你没看错第一次调用是检查、第二次调用才是切换进程id跟用户id,不然没办法访问ui的
        ...
        final MessageQueue queue = me.mQueue;// 取队列
        ...
        // 上来就是一个死循环,屌不屌
        for (;;) {
            // 取消息队列取到就往下执行,取不到轮循(并非空转、后面会讲next方法的机制)
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ...
                // Handler通过dispatchMessage分发Message
                msg.target.dispatchMessage(msg);
            ...
            // 切换原来的身份
            final long newIdent = Binder.clearCallingIdentity();
            ...
            // 用完了的Message回收
            msg.recycleUnchecked();
        }
    }

上面是我提炼出来的核心代码(我认为)
1).prepare:在当前线程初始化Looper(同一线程禁止重复调用\线程与Looper为1:1关系)
2).loop:检查并切换pid/uid,通过死循(并非一直空转)环轮询MessageQueue,通过Message中的Handler分发消息,切换回自己的pid/uid,Message对象回收进对象池
3).quiteSafely: 安全退出/非安全退出

2. Handler

// 平时如果想用主线程的Looper更新UI用它用它用它getMain(),不用再new了新版本sdk才支持getMain
   private static Handler MAIN_THREAD_HANDLER = null;
 
   public Handler(@Nullable Callback callback, boolean async) {
        ...
        // 获取当前所在线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(...);
        }
        // 取Looper中的消息队列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    } 
    // 执行消息回调
    public void dispatchMessage(@NonNull Message msg) {
        //优先执行message中的Callback,想不到吧我也有callback
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            // 否则执行Handler中的callback
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 最后才轮到handMessage我最常用的方式确是优先级最低的,衰。
            handleMessage(msg);
        }
    }
    ...
    // Message对象池取对象,没有则创建
    public static Message obtain(...){...}
    ...
    // 直接将发送自己创建Message实体
    public boolean sendxxx(){}
    // 发送回调接口让Handler帮我们创建Message
    public boolean postxxx(){}
    ...
    // 不管你是那条道,终点都在这
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        // 发送消息前将自己作为执行者赋值给Message以便于后面轮到自己的时候有人执行。
        msg.target = this;
        ...
        // 加入到消息队列,队列会根据时间排序入列
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    // 没它啥都干不了
    final Looper mLooper;
    // 执行者负责根据需求创建任务放入列表
    final MessageQueue mQueue;
    // 远程服务binder中的service,用于进程通讯 对就是用来进程通讯的
    IMessenger mMessenger;
    // 远程服务的实现
    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

Handler的主要职能就是对外暴露通讯接口(通讯起点\终点),Message对象构造回收。

  1. send/post 通讯起点
  2. obain 对象复用
  3. enqueueMessage入列消息
  4. dispatchMessage 通讯终点

3. Message

// 通讯的数据包
    Bundle data;
    // 执行者
    Handler target;
    // 下一位
    Message next;
    // 对象池(链表)中的头部
    private static Message sPool;
    // 对象池中对象数量
    private static int sPoolSize = 0;
    // 对象池中的存储上限
    private static final int MAX_POOL_SIZE = 50;
    ...//这里还有各种状态机、基础数据、进程uid等等

    // 使用缓存的空Message,没有则创建
     public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                // 取对象池(链表)中的对象,同时链表链表前移
                ...
            }
        }
        return new Message();
    }
    // 其实就是深拷贝
    ...obtain(...){...}
    // 使用完清理回收
    void recycleUnchecked() {
        // 清空状态、数据、回调
        flags = FLAG_IN_USE;
        ...
        // 加入到缓存队列中,修改计数器,溢出则不入列
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                ...
            }
        }
    }
  1. obtain:对象池出列
  2. recycleUnchecked:对象池出列
  3. flags:状态机

4. MessageQueue

    // 消息
    Message mMessages;
    // 未执行的 补充操作队列(UI刷新的dispatch执行完了 可以补充做一些处理)
    private final ArrayList mIdleHandlers = new ArrayList();
    // 文件描述符
    private SparseArray mFileDescriptorRecords;
    // 正在执行的 补充操作队列
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;

    // 是否阻塞
    private boolean mBlocked;
    ...
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        // 创建消息队列-底层队列c/c++
        mPtr = nativeInit();
    }
    ...
    // 核心方法取队列
    Message next() {
        // 底层native的消息队列头指针
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;// java队列与native队列必须同步,否则返回空结束loop轮询
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        // 下次执行的时间
        int nextPollTimeoutMillis = 0;
        // 又一个死循环不停的在queue中取消息
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                // 调用native方法查看当前是否需要挂起当前队列(CPU繁忙的情况下)
                Binder.flushPendingCommands();
            }
            // 同步native消息队列出列下一个要执行的消息
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // 可执行的同步消息
                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) {
                    // 预设定执行时间大于当前时间则取等待时间
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        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;
                        msg.markInUse();// 标记消息状态并返回该消息
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // 进程或者线程结束 销毁队列包括native队列结束轮循
                if (mQuitting) {
                    dispose();
                    return null;
                }
                ...// 取当前未执行的 补充操作队列
            }

            ...// 执行补充操作队列
        }
    }
    // 添加消息到队列
    boolean enqueueMessage(Message msg, long when) {
        ...// 消息状态判断

        synchronized (this) {
            if (mQuitting) {
                ...// 队列已经销毁退出
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            // 是否需要唤醒
            boolean needWake;
            // 空队列或者执行时间小于头部Message的执行时间
            if (p == null || when == 0 || when < p.when) {
                // 是否需要唤醒
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 唤醒条件 是否阻塞 头部没有执行者 添加的消息为异步消息
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                // 根据Message when执行时间顺序插入到链表中
                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;
            }

            // 唤醒队列,传入native队列的头指针
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
    ...//还有一些其它的代码跟队列无关,native文件描述符监听,看命名跟dns tcp网络相关这里就不过多解读了

MessageQueue作为一个队列内部的大量逻辑跟native相关,因为涉及到cpu资源利用等一系列性能问题所以使用Java队列+Native队列,比如防止死循环的空转导致大量CPU资源消耗、CPU资源紧张的时消息未到执行时间则挂起等等,ideHandler(跟Handler无关)在App启动时候因为cpu资源紧张但又想不让队列挂起所做的补充操作,文件描述符监听等等代码。
1). next: 消息出列
2). enqueueMessage: 消息入列

最后我们常常使用Handler导致内存泄漏,是因为Message会持有Handler/Callback的引用,而这两者经常是内部或者匿名类会持有外部类的引用(如Acitivty),在Message为delay操作时Acitivty退出销毁因为被Message引用而导致内存泄漏;而自线程有能够访问ui线程主要还是通过native中切换线程uid来实现的(Linux系统下线程资源访问通过uid识别),而这个由native实现 感兴趣的小伙伴可以看看Binder. clearCallingIdentity的源码。

今天就记录这么多了,第一次写技术长文有些瑕疵希望有心看到这篇记录的不要介意。

下次我来补充讲一下ThreadLocal相关的内容,因为这个东东很多小伙伴也有许多疑问。

你可能感兴趣的:(Android Handler源码阅读(技术记录/回忆))