Handler源码解析

一,Handler的使用

1)创建全局静态常量

class Cons{
     static final int MSG_WHAT_0x1000 = 0x1000;
     static final int MSG_WHAT_0x1001 = 0x1001;
}

2)创建Handler对象

private static Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case MSG_WHAT_0x1000:{
                    Log.d("MainActivity","show message-->"+msg);
                }
                case MSG_WHAT_0x1001:{

                }
                break;
                default:
                    throw new IllegalStateException("Unexpected value: " + msg.what);
            }
            return false;
        }
    });

3)Handler消息的发送

handler.sendMessage(handler.obtainMessage(MSG_WHAT_0x1000));

4)移除消息

 @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null);
    }

如上便是谷歌官方推荐的Handler使用方式,上述代码可能对一些新手有一些疑问,在此罗列(大佬请绕道):
Q1,创建Handler时使用了“new Handler.Callback()”而不是如下方式创建:

 private Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

Q2,创建Handler对象时,使用了static 对handler对象的修饰
Q3,发送消息时使用了“handler.obtainMessage”而不是直接new
Q4,在onDestroy中调用removeCallbacksAndMessages
接下来就带着以上疑问进入源码分析。

二,Handler源码流程分析

Handler的源码分析在Framework层目前主要涉及到四个类:Handler,Message,MessageQueue,Looper。
1,Looper及MessageQueue对象创建
在AndroidStudio中多次Shift键或者在线源码找到ActivityThread.java类,它便是Android的主线程也称为UI线程,Looper的初始化便在其main函数中

//见源码7310行
 public static void main(String[] args) {
        //省略若干代码
        (1)此处调用了创建了Looper对象
        Looper.prepareMainLooper();
        //省略若干代码
        (2)开启消息的轮询
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

根据(1)处的代码,进入到Looper类的prepareMainLooper函数中

//见Looper源码119行
 public static void prepareMainLooper() {
        //调用内部函数进行Looper对象的创建
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
       //创建一个Looper对象,并存入到ThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
 private Looper(boolean quitAllowed) {
        //创建了一个MessageQueue
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

上述便是Looper的创建过程了,创建了主线程中唯一的Looper对象,并存入到了ThreadLocal,在其构造函数中并创建了一个MessageQueue的消息队列。
关于ThreadLocal,它内部有一个ThreadLocalMap,类似与HashMap的数据结构,其键是当前ThreadLocal,值是要存放的属性。
在Thread.java源码中可以看到其内部有一个 ThreadLocal.ThreadLocalMap threadLocals = null;的内部变量,而我们创建的ThreadLocal最后都会存放到该Map中。
ThreadLocal存放值时其源码如下:

  public void set(T value) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程中的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        //如果map不为空,则直接存入当前值
        if (map != null)
            map.set(this, value);
        else
        //创建一个ThreadLocalMap并存入当前线程中
            createMap(t, value);
    }

2,开启Looper的轮询
在ActivityThread的main函数中可知,在创建完成Looper后,又调用了Looper.loop()函数,主要目的就是开启消息的轮询。
进入Looper的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.");
        }
        //获取Looper中的MessageQueue,在Looper创建时,会连带创建一个MessageQueue
        final MessageQueue queue = me.mQueue;
        //开启死循环
        for (;;) {
            //(1)调用MessageQueue的函数不停的从消息队列中取出消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //省略...
            try {
                //(2)调用Message中的target(即Handler对象)进行消息的消费
                msg.target.dispatchMessage(msg);
               }
        //省略...
        }
    }

(1)MessageQueue中的next函数一探究竟,发现其内部也是一个死循环,并返回一个Message对象。

 Message next() {
        //死循环,从队列中取出消息进行消费
        for (;;) {
        //省略...
        }
    }

如上只是消息的消费的过程,接下来,进入分发过程。
3,Handler消息的发送
根据示例代码中,创建一个消息时,都会调用handler对象的send或post函数,进入handler源码。

    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    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 postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    
    public final boolean postDelayed(Runnable r, int what, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
    }
    
  public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

 public boolean sendMessageAtTime(@NonNull 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);
    }

从上诉代码可知,不管是send或者post的消息最后都调用了enqueueMessage函数

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
            //关键代码,把当前handler对象赋值给Message
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //把消息按照执行时间存放到MessageQueue中
        return queue.enqueueMessage(msg, uptimeMillis);
    }

进入到enqueueMessage中,发现我们发送的消息,最后都会调用msg.target = this 把当前Handler对象存放到Message中,在上述Looper的loop轮询中调用的msg.target.dispatchMessage(msg);,就是此时存入的handler对象。

接下来我们就进入Handler中的dispatchMessage函数

 public void dispatchMessage(@NonNull Message msg) {
        //判断Message中的runable对象是否为空
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //此处就回到了第一章节中的Q1,它首先判断了我们传入的callback不为空,先调用callback的接口实现,
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //如果callback为空的话,调用内部重写函数,即传统的使用方式,最后执行。
            handleMessage(msg);
        }
    }

到此整个Handler的流程大概跑通了
4,Message源码解析

public final class Message implements Parcelable {
    /**
     * User-defined message code so that the recipient can identify
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
    public int what;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;

    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     *
     * 

Note that Parcelable objects here are not supported prior to * the {@link android.os.Build.VERSION_CODES#FROYO} release. */ public Object obj; /** * Optional Messenger where replies to this message can be sent. The * semantics of exactly how this is used are up to the sender and * receiver. */ public Messenger replyTo; /** * Indicates that the uid is not set; * * @hide Only for use within the system server. */ public static final int UID_NONE = -1; /** * Optional field indicating the uid that sent the message. This is * only valid for messages posted by a {@link Messenger}; otherwise, * it will be -1. */ public int sendingUid = UID_NONE; /** * Optional field indicating the uid that caused this message to be enqueued. * * @hide Only for use within the system server. */ public int workSourceUid = UID_NONE; /** If set message is in use. * This flag is set when the message is enqueued and remains set while it * is delivered and afterwards when it is recycled. The flag is only cleared * when a new message is created or obtained since that is the only time that * applications are allowed to modify the contents of the message. * * It is an error to attempt to enqueue or recycle a message that is already in use. */ /*package*/ static final int FLAG_IN_USE = 1 << 0; /** If set message is asynchronous */ /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1; /** Flags to clear in the copyFrom method */ /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; @UnsupportedAppUsage /*package*/ int flags; /** * The targeted delivery time of this message. The time-base is * {@link SystemClock#uptimeMillis}. * @hide Only for use within the tests. */ @UnsupportedAppUsage @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public long when; /*package*/ Bundle data; @UnsupportedAppUsage /*package*/ Handler target; @UnsupportedAppUsage /*package*/ Runnable callback; //单链表 /*package*/ Message next; /** @hide */ public static final Object sPoolSync = new Object(); //缓存池 private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; private static boolean gCheckRecycle = true; 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(); } public static Message obtain(Message orig) { Message m = obtain(); m.what = orig.what; m.arg1 = orig.arg1; m.arg2 = orig.arg2; m.obj = orig.obj; m.replyTo = orig.replyTo; m.sendingUid = orig.sendingUid; m.workSourceUid = orig.workSourceUid; if (orig.data != null) { m.data = new Bundle(orig.data); } m.target = orig.target; m.callback = orig.callback; return m; }

如上代码,可知Message其是一个单链表的数据结构。

三,调用流程图

image.png

其执行流程如下:


image.png

四,答疑

Q1,在已在dispatchMessage源码进行进行解答,这也就是为啥谷歌官方推荐使用callback的方式创建handler
Q2,在创建Handler是,大多时候都是使用的内部类的方式创建,而我们使用Handler时,基本都是在Activity/Fragment中使用的,内部类持有了上下文,会导致内存泄漏的问题,因此使用static进行修饰,当然也可以使用弱引用的方式。

Q3,发送消息时使用了“handler.obtainMessage”而不是直接new?
在Message的源码中,我们发现存在缓存池的概念,调用obtainMessage时,会先从缓存池中取,如果取不到则进行创建并加入缓存池中,这里使用到了设计模式中的享元设计,类似于RecyclerView的复用机制,因为大量的创建Message对象,存在资源浪费的情况,复用Message,可以节约内存开销。

public final Message obtainMessage()
    {
        return Message.obtain(this);
    }

Q4,在onDestroy中调用removeCallbacksAndMessages
答:主要是在页面销毁时,即时的回收消息,防止内存泄漏。

五,面试题解析

1, Looper 的 loop() 死循环为什么不卡死?
为了让主线程持续处理用户的输入,loop() 是死循环,持续调用 MessageQueue#next() 读取合适的 Message。

但当没有 Message 的时候,会调用 pollOnce() 并通过 Linux 的 epoll 机制进入等待并释放资源。同时 eventFd 会监听 Message 抵达的写入事件并进行唤醒。

这样可以空闲时释放资源、不卡死线程,同时能持续接收输入的目的。

彩蛋1:loop() 后的处理为什么不可执行

因为 loop() 是死循环,直到 quit 前后面的处理无法得到执行,所以避免将处理放在 loop() 的后面。

2,异步 Message 或同步屏障
异步 Message:设置了 isAsync 属性的 Message 实例

可以用异步 Handler 发送
也可以调用 Message#setAsynchronous() 直接设置为异步 Message
同步屏障:在 MessageQueue 的某个位置放一个 target 属性为 null 的 Message,确保此后的非异步 Message 无法执行,只能执行异步 Message

原理:当 MessageQueue 轮循 Message 时候发现建立了同步屏障的时候,会去跳过其他 Message,读取下个 async 的 Message 并执行,屏障移除之前同步 Message 都会被阻塞

总结:异步消息一般是系统内部使用的,当handler收到异步消息时,会优先处理异步消息,等到异步消息处理完后,才会处理同步消息,例如vsync信号

3,IdleHandler 空闲 Message
适用于期望空闲时候执行,但不影响主线程操作的任务

系统应用:

Activity destroy 回调就放在了 IdleHandler 中
ActivityThread 中 GCHandler 使用了 IdleHandler,在空闲的时候执行 GC 操作
App 应用:

发送一个返回 true 的 IdleHandler,在里面让某个 View 不停闪烁,这样当用户发呆时就可以诱导用户点击这个 View
将某部分初始化放在 IdleHandler 里不影响 Activity 的启动

你可能感兴趣的:(Handler源码解析)