面试题之Handler

作者: 码字农民工 | 来源:发表于2019-02-21 11:06 被阅读49次

Handler

四个重要的组件

MessageQueue,Message,Looper,Handler

源码分析

我们先从Handler的构造函数入手开始分析

//handle
public class Handler {
    public Handler() {
        this(null, false);
    }
    public Handler(Callback callback, boolean async) {
         ...
        //先去当前线程ThreadLocal中去寻找looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //将MessageQueue进行赋值
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
}

从上面中我们可以看出来如果我们在Activity中创建Handler的时候会去查询主线程中有没有Looper,而主线程中的Looper是在ActivityThread中的main方法中就早已创建的,我们就来看下ActivityThread中的代码

public class ActivityThread {
    public static void main(String[] args) {
        ...
        //这里调用Looper的prepareMainLooper静态方法创建主线程的Looper
        Looper.prepareMainLooper();
        ...
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        ...
        //这里开启Looper在主线程中的循环
        Looper.loop();
    }
}

我们从上面的代码中得知在AppActivityThread类中的main函数调用了Looper.prepareMainLooper()Looper.loop()方法,我们跟进Looper中来看看具体实现

public class Looper {
    public static void prepareMainLooper() {
        //我们主要关注这行代码
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //这里是将主线程的Looper进行赋值
            sMainLooper = myLooper();
        }
    }
    
    private static void prepare(boolean quitAllowed) {
        //这里去检测当前线程是否已经存在了Looper,如果存在则抛出异常
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //这里将当前线程中set了一个新创建的Looper,我们接着看Looper的构造函数
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
    private Looper(boolean quitAllowed) {
        //这里就进行了MessageQueue的创建并且赋值,然后我们接着上面ActivityThread中的Looper.loop()分析
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    
    public static void loop() {
        //获取当前线程中的Looper进行判断,并且取出MessageQueue进行轮训
        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 (;;) {
                //这里每次都去取MessageQueue中的下一条消息,从这里我们可以得出MessageQueue是个单链表存储
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            final long dispatchEnd;
            try {
                //我们主要看这行代码调用了这个Message中的target(就是发送这条消息的Handler)的dispatchMessage方法
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
            //同时这里将这条消息进行释放
            msg.recycleUnchecked();
        }
    }
}

从上面代码中我们可以看出Looper.prepare()中创建了Looper,并且在Looper的构造方法中创建了存储MessgeMessageQueue,并且将当前线程的Looper放到了sThreadLocal中存储。然后调用Looper.loop()进行对MessageQueue中的Message进行轮询。最终调用了Handler中的dispatchMessage()方法。我们来看看HandlerdispatchMessage()方法的实现。

public class Handler {
    public void dispatchMessage(Message msg) {
        //如果msg中的callback(也就是Runnable)不为空,则调用Runnable.run方法
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //如果在创建Handler的时候传入了callback,则调用mCallback.handleMessage方法
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //最后否则调用Handler中的handleMessage()方法
            handleMessage(msg);
        }
    }
}

从上面代码中我们可以看出来最终的消息处理是通过Runnable.run()方法或者Handler的构造函数中传入的callback或者Handler类重写的handleMessage()来进行消息处理。最后我们看看Handler.sendMessage()方法

public class Handler {
    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //我们这里可以关注一下uptimeMillis这个值是由系统当前时间+延迟时间的和决定的。这个值在Message存储到MessageQueue的时候需要用到
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //取出我们当前的MessageQueue,也就是当前线程中的Looper中的MessageQueue
        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) {
        //将Handler放入Message中的target变量
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //最终调用MessageQueue中的enqueueMessage()方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }
}

那么接下来我们看看MessageQueue.enqueueMessage()方法是如何对message进行存储的。

public class MessageQueue {
    //调用MessageQueue.enqueueMessage方法将Message进行存储
    boolean enqueueMessage(Message msg, long when) {
        //先去各种判断
        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;
            //这里进行判断是否是存入的第一条Message
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                ...
                Message prev;
                //这里又开启一个死循环,这里的逻辑有点绕
                for (;;) {
                    //将当前的message赋值给prev
                    prev = p;
                    //将p的下一条message赋值给p
                    p = p.next;
                    //如果下一条的message为空,或者是下一条message的when大于当前传入的message的when,就进行返回
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //将当前的message的下一条message进行赋值
                msg.next = p; 
                //再将原先的message的下一条消息设置为传入的message
                prev.next = msg;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
}

总结

通过以上的源码分析我们这边的思路也变得更加清晰,对于以后面试中问到Handler应该也可以说是游刃有余。我们现在可以再回顾一下整体的流程:先调用Looper.prepare()来创建Looper,同时在Looper的构造方法中创建了MessageQueue,然后调用Looper.loop()方法来对MessageQueue中的Message进行轮询,最终调用了Message.target变量(也就是Handler)的dispatchMessage()方法来进行handleMessage()处理消息,回到Handler,在Handler的构造方法中我们获取了当前线程的Looper,找到其中的MessageQueue进行绑定,然后我们调用sendMessage方法来进行消息发送,最终都会调用到Handler中enqueueMessage方法,在这个方法中调用了MessageQueue中的enqueueMessage()方法来进行消息存储。这里我们需要注意以下几点:

  1. Looper类主要是为每个线程开启单独的消息循环
  2. Hanlder是Looper的一个接口
  3. 在非主线程中直接new Handle()是不可以的,需要先去创建Looper

相关文章

  • Handler

    Handler 简单使用Handler Looper MessageQuene 源码解析面试题 Handler...

  • Android超实用最全面试大纲(二)

    文章目录: Handler面试题 AsyncTask面试题 HandlerThread面试题 IntentServ...

  • Android最全面试大纲(二)

    文章目录: Handler面试题 AsyncTask面试题 HandlerThread面试题 IntentServ...

  • Handler面试题总结

    面试题总结 Handler是一个比较重要的东西,所以把网上发的Handler中的面试题总结了一下,这些面试题没问题...

  • 面试题之Handler

    Handler 四个重要的组件 MessageQueue,Message,Looper,Handler 源码分析 ...

  • Android 面试复习资料

    Android面试题总结 1.handler 1.handelr机制2.子线程创建handler需要注意启动loo...

  • Handler源码分析

    Handler对于Android开发者再熟悉不过了,也是面试题的常客了,所以了解Handler机制的源码就很有必要...

  • Android面试题

    3.13日上午巨人学校面试题 Handler?同时创建两个handler,为什么消息不会串? 事件分发?点击事件和...

  • Android消息机制Handler源码分析

    Handler对于Android开发者再熟悉不过了,也是面试题的常客了,所以了解Handler机制的源码就很有必要...

  • Android: 消息机制与handler面试题题解

    前言 本文主要总结Android消息机制,以及自问自答一道相关面试题。 目录 面试题Handler.postDel...

网友评论

    本文标题:面试题之Handler

    本文链接:https://www.haomeiwen.com/subject/wtnlyqtx.html