Handler源码流程解析,面试常客!!傻白甜式阅读体验!

handler机制属于面试常客啦,看懂handler源码有助于我们理解使用。网络上的源码解析也有很多,这篇是自己边看源码边记录的博客,加深印象。本篇将会从handler的使用流程出发,也是分享自己是如何看源码的,当然,平时我看源码就是先百度看其他大佬的源码解析,然后在自己看源码比较好,最后在自己总结回顾下。这最后一步很早就知道,只是之前并不知道他的重要性~

本文内容图解

handler机制无非就是讲解Handler,Looper,messageQueue,Message之间是如何协调工作的。

handler机制成员

Handler 发送/处理消息

MessageQueue 消息队列,存储消息

Looper 循环器,从消息队列取消息发送給handller

message 消息  

先来张自制流程图感受下~^_^大概的流程

使用流程

 private Handler hanlder = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });




        //发送消息方式1
        Message message = Message.obtain();
        message.what = 1;
        message.obj = "sss";
        hanlder.sendMessage(message);

        //发送消息方式2
        hanlder.post(new Runnable() {
            @Override
            public void run() {

            }
        });

handler的创建

点击Handler进去,如下图

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

内部调用的是两个参数的,ctrl按住,点击this看看这个构造函数的代码。

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

第一个if代码块看不懂?没关系,看他的Log猜一下,应该是判断创建的log是否是static或者使用弱应用(一般Handler是内部类的时候都会这么提示一下,这是为了避免内存泄漏!)

 

Looper的创建

好嘞~现在看mLooper = Looper.myLooper();这个是得到looper对象,通过Looper.myLooper()得到。好奇的话可以点进去在瞅瞅,点击myLooper()进入看看说明。

/**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

看上面英文说明,返回一个与当前线程关联的Looper对象,如果当前线程没有关联的Looper,就返回null。ok,点到即止,我们知道了mLooper = Looper.myLooper();是获取与当前线程关联的Looper对象了。回到上面的Handler构造函数中,往下看,判断mLooper是不是空的,是空的就抛出异常。抛出啥异常嘞?,看下面

Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()"

翻译就是说,当前线程还没调用Looper.prepare(),所以不能在当前线程创建Handler。

handler创建部分小结:

1、调用mLooper = Looper.myLooper()方法获取到与当前线程关联的looper对象,

2、看myLooper的源码得知,返回空就是当前线程没有looper对象啦。

3、如果当前线程没有Looper对象,就是myLooper返回空的时候,会抛出异常,说当前线程还没调用Looper.prepare()方法。

得出结论,要创建Handler对象必须先在当前线程得到looper对象,要得到与当前线程关联的looper对象,要先调用Looper.prepare();方法。

那么创建一个Handler对象首先构造函数中是获取了与当前线程关联的looper对象,往下看代码,还有获取了mQueue消息队列。

额外小结一:创建looper对象有2种方式:1.Looper.prepare();2.Looper.prepareMainLooper()这俩有啥不同呢?

prepare方法是创建与当前线程关联的looper对象。

prepareMainLooper是创建与UI线程(主线程)关联的looper对象

为什么UI线程没有调用prepare方法也能创建好一个Handler不报错?

因为在UI线程启动过程中,源码内部已经帮我们调用了Looper.prepareMainLooper()方法,所以不用我们自己在去创建了。

额外小结二:回顾上面Handler构造函数中是这样获取消息队列的。

mQueue = mLooper.mQueue;

MessageQueue消息队列怎么来的

那么消息队列在Looper中是如何拿到的呢?可以看到是调用已经获取到的mLooper对象中的mQueue。mLooper对象可以在prepare中创建。那就去prepare中看看。

/** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    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));
    }

上面这段就是prepare的源码。说明翻译:在实际开始循环(消息队列循环)前,调用prepare()方法可以将当前线程初始化为一个looper对象,这可以让我们创建一个与looper关联的handler对象。注意下这里有一个可能抛出的异常,

Only one Looper may be created per thread

说明一个线程只有一个与之关联的looper!!!一对一关系!!!

重点是看下创建的代码哈,new Looper(quitAllowed),点击new Looper进去看看代码;

 /**
     * Return the {@link MessageQueue} object associated with the current
     * thread.  This must be called from a thread running a Looper, or a
     * NullPointerException will be thrown.
     */
    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

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

发现了啥,在Looper的构造函数中,直接创建了一个消息队列!!O(∩_∩)O哈哈~,吃个饭回来,差点把我搞蒙蔽了,如果你看到这里有点懵逼?慢慢看,理解下前面的逻辑,就比较清晰了。在创建looper对象时,顺带把messageQueue一起创建了。我们知道looper是与线程关联的,那么由looper创建的messageQueue也间接跟线程关联了。为啥这么说,看上面的代码中 myQueue方法。返回一个与当前线程关联的消息队列呀。。。

Looper.loop()方法

创建完Handler之后,我们需要调用mLooper.loop();方法。loop()方法很长,这里抽取部分代码

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
            
        

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

           

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
          
        
            msg.recycleUnchecked();
        }
    }

抽取什么代码呢?抽点我能看得懂的哈哈哈,,,,

1、首先通过myLooper()方法获取到looper对象,

2、在通过该对象获取到消息队列queue。通过for死循环获取队列中的消息msg,

3、然后调用msg.target.dispatchMessage(msg)方法将msg分发出去。

大概就是这么个意思。简单来说loop()方法就是不断的循环分发消息。

dispatchMessage源码解析

这里msg.target是什么呢、我们点击Message去看看源码

/*package*/ Handler target;

啊哈~是一个Handler。也就是通过handler调用dispatchMessage(msg)方法。 我们找到Handler类,在里面找到dispatchMessage方法

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

简短的源码还好读嘞~说明翻译是在这里处理系统消息。

如果msg.callback不为空,就调用handleCallback(msg)方法,点击handlerCallback方法进去看看

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

调用了massage.callback.run()方法,这里的callback我们也通过查看message的源码看看

 /*package*/ Runnable callback;

是一个runnable对象!想到了啥?我们发送消息的方式是不是有2种:

1、handler.sendMessage(Message msg)

2、handler.post(Runnable r);

而第二种post是传入一个Runnable对象!

post方式发送消息

这里简单看看handler.post方法源码

/**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

post调用了sendMessageDelayed方法,上面英文说明翻译:调用该方法导致runnable r 会被添加进消息队列。这个runnable 将在handler依赖的线程上运行起来。(如果handler是主线程,runnable就会在主线程上跑,就这个意思。)

继续看sendMessageDelayed方法中先调用了getPostMessage方法。进去瞅瞅~

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

可以看到传入的是一个runnable对象,返回的是一个message对象!!代码中做了啥呢?首先创建一个message对象,对message对象的属性callback赋值传进来的参数r,返回该message。很简单,说白了就是把runnable对象用一个message包装下~返回上一段代码,post方法中调用了

sendMessageDelayed(getPostMessage(r), 0);

就是传入了一个Message对象和一个数字0,进入该方法看看,

/**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

这段代码读起来也是香甜可口,通俗易懂呀~(*^▽^*)。首先看代码内容就知道了,发送meg消息。就这件事,在看看英文说明,等待指定时候后,将该消息加入消息队列,(哦,原来sendMessageAtTime方法就是把msg加入到消息队列),你将会在消息队列依赖的线程中,在与之绑定的handler的方法HandlerMessage中遇到他。什么意思呢?把msg消息发送到消息队列,消息队列是绑定了某个线程吧~比如绑定了线程A,那么线程A中的Handler会重写handlerMessage方法,该方法中有个Message参数咯,就是这个msg啦。

小结一下回顾一下,我们刚刚分析了loop()方法,就是不断的获取消息,分发消息,那分发消息就是调用dispatchMessage()方法,在这个方法中我们先判断要分发的msg的callback属性是不是空的,不是空的就说明是这个消息是通过Handler.post()方法传入的消息队列的。

ok,回到dispatchMessage方法中,如果callback不为空,调用handleCallback方法

if (msg.callback != null) {
            handleCallback(msg);
        } 

进入看看

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

他回调了callback的run方法~就是执行了我们runnable中实现的run方法啦~\(^o^)/~

回来继续看dispatchMessage方法,重新贴一下代码

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

如果msg.callback为空,那我们去看看mCallback是不是空的。mCallback是handler的一个Callback变量。细心的朋友就知道在哪里获取啦,上面代码有贴过哦~~

 

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

看倒数第三行~~是滴~我们在Handler创建的时候可以传入一个callback。本文的第一段代码,创建handler的方式就是传入了一个callback。如果handler的mCallback不为空,那就调用mCallback的handlerMessage即可。然后直接return掉~

在回头看看dispathMessage方法,

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

前面的都解释了,现在看看,如果msg的callback为空,并且mCallbaak也为空,那就调用handleMesage方法。这个方法是handler内部的方法。

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

可以看到是一个空方法,并且英文说明:子类必须实现这个方法来接收消息!我们在代码中如果这么写,就会调用这个方法啦:

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

但是这种方法存在内存泄漏问题,一般不这么些~看看这么写会有啥提示

看到这段代码想到了啥?上面博主有写到哈~ 一般Handler是内部类的时候都会这么提示一下,这是为了避免内存泄漏!

好嘞~分发消息到handler之后就是handleMesssage处理消息了,之后流程就结束啦,是不是很简答~

最后看下消息的发送方式。其实上面提到了。

        //发送消息方式1
        Message message = Message.obtain();
        message.what = 1;
        message.obj = "sss";
        hanlder.sendMessage(message);

        //发送消息方式2
        hanlder.post(new Runnable() {
            @Override
            public void run() {

            }
        });

 消息发送分为sendMessage和post两种。当然sendMessage和post还可以再细分

首先我们看sendMessage方式

我们通过Message.obtain方式创建消息。这是推荐的创建Message对象的方式,点击ontain看下源码啦

 /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     *
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            //sPool是一个静态类Messag,
            if (sPool != null) {
                //spool指向下一个message
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                //看变量名字,猜:这个全局池是有数量上限的,每增加一个池的数量就减1
                sPoolSize--;
                //返回的是原来spool,可以看到并没有去new 一个message,减少内存开销
                return m;
            }
        }
        //spool为空就new一个
        return new Message();
    }

英文说明:为了避免创建太多实例,内部是有一个全局池,从全局池返回一个message对象。

post方式上面也提到啦,就是发送一个runnable对象,内部是将其封装为一个message对象然后添加到消息队列~

全文小结~

1、首先,我们在创建Handler前,要先调用Looper.prepare()方法,调用该方法创建与当前线程关联的loop对象,并且创建了消息队列。

2、然后我们才可以创建Handler对象,handler构造函数默认获取与当前线程关联的loop对象和消息队列,由此handler间接关联了当前线程,(额外小点:内存泄漏的原因:handler间接持有activity的引用,导致activity无法关闭,内存无法释放导致内存泄漏。)

3、调用looper.loop循环取出消息队列消息,dispatchMessage发送給各个Handler处理。

4、发送的方式分为2种,sendMessage和post方法。

补充:

1、消息队列不是队列,是一个单链表

2、消息是如何被准确的发送到对应的handler中去处理的?

回答:回顾源码就知道,消息队列取出消息后,在loop()循环中取出消息后调用: msg.target.dispatchMessage(msg);。

这句话就是讲代码发送到指定的handler去处理,这里的target就是具体的handler对象。所以只要找到这个target是什么时候被赋值的,就能回答这个问题了。这里举个栗子~比如handler.sendMessage(new Message());

我们去sendMessage中查看:

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

发现他回调:sendMessageDelayed()方法,那就在进去看看

 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

回调sendMessageAtTime方法,继续看

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

终于到了,msg.target = this,是吧, 这里的this就是handler对象了。即是我们调用handler.sendMessage中的handler了。

3、在loop中执行死循环为什么不造成ANR:

目前我看了众多文章,我的理解:首先我们要理解ANR的概念,application not response,造成ANR的原因(条件):1.事件未处理,可能被阻塞了,没有机会处理。2、在处理了,但是规定事件内还没完成。比如:activity中5秒内没完成处理,brocast中10s内没完成,service20s内没完成都会造成anr。

那么在此内完成任务即便是死循环anr也ok,因为没达到anr条件。loop中是可能造成anr的。

4、loop()中执行了死循环,那主线程的生命周期是如何被调用的?

首先我们需要明白一点,主线程不是唯一的线程,当需要的时候,由其他线程通过handler发送消息,让handler处理指定的线程生命周期。

5、loop()死循环会消耗大量资源吗?

不会,消息队列中没有消息就会阻塞,阻塞时处于休眠状态,并不会大量消耗资源。

ok~更多更仔细的回答,可以看看:https://www.zhihu.com/question/34652589

 

最后再次看下本文内容图解:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值