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}.
     *
     * <p>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其是一个单链表的数据结构。

三,调用流程图

在这里插入图片描述
其执行流程如下:
在这里插入图片描述

四,答疑

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 的启动

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值