为了账号安全,请及时绑定邮箱和手机立即绑定

从源码解析Handler、Looper、Message以及MessageQueue关系

标签:
Java Android

面试常客Handler,几乎所有的面试官都会问Handler的运行机制、四个组成部分以及其源码。今天我从源码的角度来分析下handler内部的机制原理。

Handler由四部分组成,Handler、 Looper、 Message、 MessageQueue

我们先来看Looer。

Looper

Looper中有两个主要的方法,looper.prepare() 以及looper.loop()。

looper.prepare():

    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方法中,先有一个sThreadLocal对象,从这个对象中我们获取只先进行了一个非空判断,如果此对象不为空,则抛出异常。然后如果为空,则往sThreadLocal对象中set一个new出来的Looper对象。

先来解释下sThreadLocal这个对象,其实这个对象就是一个ThreadLocal类,可以在线程中存储对象,我们通过这个类将Looper这个对象存储进去。

其次来解释下这个非空判断。如果从线程中取出这个对象不为空,则抛出异常,这一重判断表明了looper的一个重要原理,即一个线程当中只能拥有一个looper对象,如果有多个则会抛出异常。

我们再来看下new Looper()对象的构造方法源码

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

从源码得知,在Looper构造方法中实例化了一个MessageQueue对象。

到此,prepare()这个方法讲解完毕。

looper.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;        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {                // No message indicates that the message queue is quitting.
                return;
            }            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }            final long traceTag = me.mTraceTag;            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }            try {
                msg.target.dispatchMessage(msg);
            } finally {                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

我们可以看到,在loop()方法中首先调用了一个myLoop()方法,我们看下这个方法的源码

    public static @Nullable Looper myLooper() {    
        return sThreadLocal.get();
        
    }

从源码可以看出,myLooper这个方法中从ThreadLocal线程拿到了之前我们在prepare()set进去的looper对象。所以loop()方法首先调用myLooper()方法获取looper对象,之后又做了一个为空判断,如果为空抛出异常。由此句代码标明,loop()方法必须在prepare()方法之后才能调用,因为必须要先用prepare()方法创建一个Looper对象,不然会抛出异常。

之后通过me.mQueue拿到一个MessageQueue对象queue,之后用for进行了死循环,通过queue.next()不断从消息队列中拿出Message消息,如果消息为空则跳出循环,如果不为空则调用msg.target.dispatchMessage()方法,把这个获取出来的消息交给这个方法去处理。其实这个方法就是handler,这个我们后面讲到了handler再说,这里不展开。最后调用msg.recycleUnchecked();消息回收。

至此Looper两个主要的方法讲完。

我们来总结下:

首先调用looper.prepare()方法来创建一个Looper对象,并将这个对象存放进ThreadLocal线程当中去,并调用if判断,确保线程中有且仅有一个loopr对象。

之后调用looper.loop()方法先取得存进线程中的looper对象,之后通过looper对象获取MessageQueue消息队列对象queue.然后进行for循环,注意,这里是一个死循环,从消息队列中不断next()拿出Message消息,并且将消息交给msg.target.dispathcMessage去处理,最后进行回收处理。

Handler

我们在使用handler的时候,一般会先去new一个handler对象,所以我们先看下handler的构造方法。

    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 that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

从构造方法可以看到,通过myLooper()方法获取到了looper对象,之后进行非空判断,确保在使用的时候有这个looper对象。之后通过这个looper对象获取MessageQueue对象。这样确保了这个handler和之前调用Looper.prepare()中new出来的MessageQueue为同一个消息队列。

之后我们看下 sendMessage() 方法。

public final boolean sendMessage(Message msg)  
 {  
     return sendMessageDelayed(msg, 0);  
 }
 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
     Message msg = Message.obtain();  
     msg.what = what;  
     return sendMessageDelayed(msg, delayMillis);  
 }
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);  
   }

这个方法最后是将获取到的MessageQueue对象存放到了enqueueMessage()方法中去,并将他返回。

我们再来看enqueueMessage()这个方法里面做了什么事情

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }        return queue.enqueueMessage(msg, uptimeMillis);
    }

这里注意到,一开始将this对象赋值给了msg.target。还记得之前在分析looper.loop()源码的时候,之后将MessageQueue取出来的消息交给了一个叫msg.target.dispathcMessage去处理吗。其实这里的this就是handler对象,等于说把handler对象赋值给了msg.target对象去,最后将这个对象添加到消息队列的enqueueMessage()方法中去。等于说,handler发送出来的消息最后全都放到了消息队列当中去。

我们再来看下 dispatchMessage() 这个方法:

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

我们看到最后调用了一个我们常常在使用handler的时候覆写的一个方法handleMessage()

我们来看下这个handleMessage()方法源码

    public void handleMessage(Message msg) {
    }

发现里面是空的,这代表什么呐?这代表这个方法Android什么也没帮我们干,我们可以自己根据自己的业务逻辑来写入相应的逻辑代码。这也就是平时我们写代码的时候要覆写这个方法,并且在这个方法中写业务代码的原因。

至此handler的源码也看完了,我们来总结下:

我们在使用handler的时候首先会new一个handler对象,handler构造方法里面会从mylooper()方法中获取一个looper对象,并且用looper对象去调用MessageQueue来获取同一个线程中的消息队列,确保Looper.prepare()中new的MessageQueue和handler中的消息队列为同一个。

之后通过sendMessage()方法发送消息。这个方法最终调用的是sendMessageAtTime()方法。这个方法最终是将handler发送的消息添加到消息队列MessageQueue当中去。最后调用dispatchMessage方法来处理这些消息。最终处理这些消息的业务代码放在了一个handMessage()空当中由程序员自行根据自身的业务逻辑去编写代码。

总结

Handler机制的原理如下:

先通过Looper.prepare()创建一个looper对象并将他添加进入ThreadLocal线程当中。通过if判断,确保一个线程中有且仅有一个looper对象。

之后通过Looper.loop()从ThreadLocal线程中获取looper对象,并且调用looper.queue对象获取到消息队列MessageQueue,利用死循环不断从消息队列中获取消息,交给msg.target.dispatchMessage去处理。

此时handler中,通过构造方法从线程中获取looper对象,并通过这个对象获取消息对应的消息队列,确保消息队列和之前的looper.loop()调用的是同一个。

之后sendMesage将发送的消息存放到消息队列MesageQueue中。然后在dispatchMessage中调用了handMessage()方法去处理MessageQueue中的消息。至此,Handler全部原理结束。

但是我们在使用Handler的时候,并没有使用loop.prepare()创建looper,也没有用looper.loop()来启动looper啊!其实Android的主线程中就已经有了一个looper对象,所以不需要我们自己去创建这个对象,如果是子线程中那就需要我们自己去创建处理了,代码如下:

    new Thread()  
        {  
            private Handler handler;  
            public void run()  
            {  
  
                Looper.prepare();  
                  
                handler = new Handler()  
                {  
                    public void handleMessage(android.os.Message msg)  
                    {  
                        Log.e("TAG",Thread.currentThread().getName());  
                    };  
                };  
                
                Looper.loop();

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
3
获赞与收藏
41

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消