浅谈Handler消息处理机制

1.什么是Handler

咱们平时在开发中,常常用到Handler,用来发送消息,处理消息。 或者作一些延迟发送消息,跨线程发消息,或者更新UI,或者去实现一些定时轮询的操做。 安卓发展至今,已经有不少框架,能够代替这样原生Handler的通讯方式。 好比Eventbus, Rxjava,AyncTask...等等。可是实际上底层依然是对Handler的封装。那么Handler到底是什么?java

简而言之:面试

Handler就是Android系统提供给咱们,用来更新UI的一套机制,也是一套消息处理机制。经过Handler,能够用来发送消息,处理消息。若是不遵循这样的机制,就没有办法更新UI,会抛出异常。框架

2. Handler消息机制的五个重要角色

1.Handler: 消息的发送和处理者async

2.Message: 消息ide

3.MessageQueue: 消息存放的队列oop

4.Looper : 从消息队列里面一条一条取消息的消息侦听器,或者消息泵post

5.线程: 当前是在哪一个线程学习

3.通俗的理解这个消息机制

也许不少人在网上都看过不少资料去解释这样一个消息机制,可是若是向别人阐述这样一个原理的时候,或者面试的时候,我相信不少人仍是模棱两可,弄不太清楚。 那么,经过一个简单的生活案例,来帮助理解一下:ui

eg: 咱们都知道读书的时候,咱们常常须要向老师请假之类。this

好比:报告老师,我要上洗手间。而后老师说:容许,快去吧。
过了一会。
又给老师报告:报告老师,后面有同窗踢我凳子。老师说:你先坐前面来,不要理他。

固然,这只是比喻。咱们经过这个比喻,来比较生动形象的去理解handler就容易多了。

1.handler:学生。

2.message:报告老师的内容

3.Looper :老师本身

4.messagequeue: 老师的耳朵和听力记忆
解释:

*   学生(handler)向老师(Looper)举手

*   说 “我要上洗手间”这个报告(message),

*   而后老师的耳朵(messagequeue)听到了这个报告,

*   先是反馈了学生的请求(dispatchHandle),告诉这个同窗,赞成他的请求,

*   因而学生本身就上洗手间去了(handleMessage)

        ps:      老师:     在这个过程当中,就是一个消息的接受者,源源不断的接受学生的各类报告或者消息,存在老师的记忆里,         老师又没有分身术,只有一张嘴,因此同一时间,只能根据记忆中的消息,一条一条的反馈给学生,处理他们的请求。       学生:       在这个过程当中,咱们能够发现,学生既是消息的发出者,又是消息的处理者。      收到老师的赞成后,因而就高高兴兴的去上洗手间去了。    

整个过程如图:

由图能够看出:
1.handler负责发送消息,接收处理消息
2.Looper负责接收handler发过来的消息
3.MessageQueue就作为Looper消息泵内部的消息容器 3.Looper在内部,将发送过来的消息,交给自身的消息队列,并按时间顺序加入其中 4.Looper在内部,经过不断循环的方式,将消息从队列中一个一个取出,回传给handler

此时你有可能会问:那么Handler它怎么知道应该往哪发消息,而且发给哪一个Looper,加入这个Looper的消息队列呢?答案在构造方法中。

4.Handler的建立和Looper的关联关系

Handler的建立,调用Handler的构造方法便可。 new Handler(). 那么咱们看下Handler的构造方法都作了些什么呢?Handler的构造方法有好几个,咱们先从空构造看起:

    //从这开始看起,空构造  
    public Handler() {  
        this(null, false);  
    }  
    public Handler(Callback callback) {  
        this(callback, false);  
    }

    public Handler(Looper looper) {  
        this(looper, null, false);  
    }

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

    public Handler(boolean async) {  
        this(null, async);  
    }

   //而后看到这个构造方法  
    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());  
            }  
        }  
        //构造方法内部,有一个默认的looper对象,而且这个对象是经过myLooper获得,也就是当前线程的looper。  
        //当前线程:默认状况就是主线程,为何会是主线程,后面会讲  
        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;  
    }

    public Handler(Looper looper, Callback callback, boolean async) {  
        mLooper = looper;  
        mQueue = looper.mQueue;  
        mCallback = callback;  
        mAsynchronous = async;  
    }

上面代码能够看出,在handler的默认构造器中,有一句:mLooper=Looper.myLooper(). 这说明Handler 默认的时候,有一个Looper对象。可是这个Looper对象是怎么建立得来的呢?点击去发现:就是根据当前线程返回一个looper对象。当前线程是什么呢?默认状况下,就是主线程,也就是MainLooper.

 /**  
     \* Return the Looper object associated with the current thread.  Returns  
     \* null if the calling thread is not associated with a Looper.  
     */  
     //解释一下:根据当前线程,返回looper对象。若是线程没有与之关联的Looper,那么返回空  
    public static @Nullable Looper myLooper() {  
        return sThreadLocal.get();  
    }

好了,看到这里,咱们就看到了Handler和一个默认looper对象如何关联的。至于looper对象的如何建立,后面再详细讲解。
咱们能够得出结论:

*   默认状况下:new Handler()中的looper对象是主线程的looper对象 ,那么消息就是发给主线程的handler进行处理。

*   须要和特定线程的Handler通讯,咱们就须要调用new Handler(Looper looper) ,传入特定线程的Looper对象便可。

*   Looper对象是属于什么线程,那么handler,就是往哪一个线程进行发送消息和处理消息。

5. 发送消息的过程

搞清楚Handler和Looper如何进行关联的关系之后,咱们从消息的发送开始理解。
Handler 如何发送消息,消息从哪里产生? 要搞清楚消息的产生,咱们首先要知道handler发送一条空消息是sendEmptyMessage().那么咱们就跟踪这个方法: 方法的参数what ,就是一个标记位,先无论它。

  /**  
    public final boolean sendEmptyMessage(int what)  
    {  
        return sendEmptyMessageDelayed(what, 0);  
    }

再继续往下跟踪

//这里能够看到,sendEmptyMessage()默认会调用sendEmptyMessageDelayed()方法  
 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
 //内部,经过Message.obtain(),产生了一条消息Message  
        Message msg = Message.obtain();  
        msg.what = what;  
        //而后再调用sendMessageDelayed()方法  
        return sendMessageDelayed(msg, delayMillis);  
    }
 public final boolean sendMessageDelayed(Message msg, long delayMillis)  
    {  
        if (delayMillis < 0) {  
            delayMillis = 0;  
        }  
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
    }

这里能够看到发送空消息时,Message是在方法内部,经过Message.obtain产生的。 而且会层层调用,最终是这么一个方法路径:

sendEmptyMessage-->sendMessageDelay-->sendMessageAtTime ,最终都是调用sendMessageAtTime()来发送消息的。

6.消息发出后的过程

经过上面sendMessageAtTime()发送消息,消息产生后,按照前面的图的理解,它是会发送出一个Messgae,而且发给Looper,由Looper加入到消息队列的。那么代码上是怎样的呢?

 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
        //1.首先获取到queue对象,  
        MessageQueue queue = mQueue;  
        if (queue == null) {  
            RuntimeException e = new RuntimeException(  
                    this + " sendMessageAtTime() called with no mQueue");  
            Log.w("Looper", e.getMessage(), e);  
            return false;  
        }  
        //2\\. 将消息,插入到这个消息queue队列中去,而且附带了一个时间值  
        return enqueueMessage(queue, msg, uptimeMillis);  
    }

咱们能够看出:
在最终发送消息的地方,拿到了Queue队列对象,而后就把消息加入到了这个Queue队列中去了。 这个Queue对象的获取,咱们能够观察handler的构造看到:每一次Handler建立的时候,会拿到looper对象,再经过looper对象来获取到内部的这个Queue队列对象。回顾handler的建立可发现,以下:

  public Handler(Callback callback, boolean async) {  
        ....  
        ....  
        //1先拿到线程的looper  
        mLooper = Looper.myLooper();  
        if (mLooper == null) {  
            throw new RuntimeException(  
                "Can't create handler inside thread that has not called Looper.prepare()");  
        }  
        //2经过looper获取到消息queue队列  
        mQueue = mLooper.mQueue;  
        mCallback = callback;  
        mAsynchronous = async;  
    }

再看下消息加入queue队列的过程

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
        //这里能够发现 消息msg的target属性:是this,也就是handler 自身当前对象  
        msg.target = this;  
        if (mAsynchronous) {  
            msg.setAsynchronous(true);  
        }  
        return queue.enqueueMessage(msg, uptimeMillis);  
    }

在加入队列的时候,能够看到消息msg的地址,也就是处理者target(由谁来最终处理消息),赋值为this。
这也就说明了,handler默认状况下,发送消息的是它本身,处理消息的也是它本身。
总结一下上面消息的发送过程:

*   发送出去 sendEmptyMessage(),消息的产生在这个里面

*   调用sendEmptyMessageDelay()

*   内部调用sendMessageDelay()

*   内部调用sendMessageAtTime()

*   内部经过当前线程,首先获取到mQueue对象,也就是 消息队列对象MessageQueue

*   当queue不为空的状况下,设置 target,发送给谁, 默认是this --handler本身

*   而后把消息message,放到消息队列中。queue.enqueueMessage(消息,时间);

####7. 侦听消息和取消息的过程
咱们知道,消息发送到Looper中后,就进入到了队列中,而后等待Looper的循环取出进行分发。那么Looper.loop() 这个循环的过程是在何时触发的呢?因为Android默认的线程是主线程,因此在应用进程启动以后,就会进入ActivityThread这个主线程中,而在这个ActivityThread中的main()方法,就会触发Looper.loop()开始侦听主线程的消息。
 //main方法
 public static void main(String[] args) {

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        
        //1. 触发loop
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    
能够看到,在主线程的ActivityThread main()方法内部,触发Looper.loop()。学过java的人都知道,main方法是一个类的主入口.
那么loop是如何进行取消息的呢?点进去继续往下看

 public static void loop() {  
        // 1.拿到当前线程looper对象  
        final Looper me = myLooper();  
        if (me == null) {  
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
        }  
        2\. 获得 looper对象内部的消息队列queue  
        final MessageQueue queue = me.mQueue;  
 ...  
 ...  
        //3.死循环,配合队列进行取消息  
        for (;;) {  
        //4.取出一条消息  
            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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;  
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {  
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));  
            }  
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();  
            final long end;  
            try {  
             //4.分发消息  
                msg.target.dispatchMessage(msg);  
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();  
            } finally {  
                if (traceTag != 0) {  
                    Trace.traceEnd(traceTag);  
                }  
            }  
            
        }  
    }

能够看到:

Looper.loop()方法中

2.经过myLooper,拿到一个Looper对象

3.取出looper中的消息队列 mQueue

4.而后经过一个死循环,里面经过mQueue.next来取消息
若是消息为空了,就return掉,

5.若是消息不为空, 就会调用
msg.target.dispatchMessage(消息)。将消息发出去
msg.target:就是handler本身
handler.dispatchMessage(消息)方法,将消息回调到dispatch中去

消息如何分发给handler处理的呢

经过上面Looper取消息的过程。咱们看到了。消息在最终取出后,会经过 dispatchMsg进行回传,交给处理者。  这个处理者,被赋值在Msg.target中. Msg的target本质上就是handler。
在前面发送消息,将消息加入队列的时候,咱们看到过,msg.target=this,是在那个时候将消息的处理者进行了赋值。
因此这里,当取出消息后,就又经过handler,经过它的dispatchMessage(msg)进行回传的。

     }  
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();  
            final long end;  
            try {  
            //消息分发,回传给处理者 。也就是handler本身。  
                msg.target.dispatchMessage(msg);  
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();  
            } finally {  
                if (traceTag != 0) {  
                    Trace.traceEnd(traceTag);  
                }  
            }

继续往下跟踪能够发现:

  /**  
     \* Handle system messages here.  
     */  
    public void dispatchMessage(Message msg) {  
        //若是消息的callback 处理者不为空,就经过这个callback进行处理。这个callback是什么呢? 后续再讲解  
        if (msg.callback != null) {  
            handleCallback(msg);  
        } else {  
        //默认状况下,走这个分支  
            if (mCallback != null) {  
            //若是全局的callback不为空,就会执行这里,再也不执行handlemessgae  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            // 回传给消息处理者,处理消息,  
            handleMessage(msg);  
        }  
    }

分发消息后,默认会进入第二个分支。一般状况下callback是没有进行设置,因此直接就会回调handleMessage(msg).走完整个流程.进入处理消息流程
但是这个msg.callback是什么呢? 还有全局的mCallback又是什么呢? 分发消息的时候,若是这些不为空。又会触发什么呢?继续日后看

handler中的Callback又是什么

因为咱们知道通知UI刷新的机制有4种方式:

view.post(runnable)  
handler.post(runnable)  
runOnUiThread(runnable)  
handler.sendMsg(runnable)

咱们跟踪一下view.post(runnable).进入view源码:

//传入一个runnable  
   public boolean post(Runnable action) {  
        final AttachInfo attachInfo = mAttachInfo;  
        if (attachInfo != null) {  
        //而后将runnable,交给handler的post执行  
            return attachInfo.mHandler.post(action);  
        }

        // Postpone the runnable until we know on which thread it needs to run.  
        // Assume that the runnable will be successfully placed after attach.  
        getRunQueue().post(action);  
        return true;  
    }

而后进入到handler的内部中post

public final boolean post(Runnable r)  
    {  
       return  sendMessageDelayed(getPostMessage(r), 0);  
    }  
      
复制代码能够看到runnable,被包装进入到了发送消息的getPostMessage(r)方法中。跟进去:  
   private static Message getPostMessage(Runnable r) {  
        Message m = Message.obtain();  
        //看到没!!! callback,原来就是指 咱们使用view.post(runnbale)的这个对象  
        //这个runnable,会赋值给msg.callback。  
        //因此当咱们使用别的方式进行更新UI或者消息通讯的时候,在handleMessage前,就会进入callback不为空的判断  
        m.callback = r;  
        return m;  
    }

也就是这里msg.callback: 再贴一遍代码

  /**  
     \* Handle system messages here.  
     */  
    public void dispatchMessage(Message msg) {  
        //若是消息的callback 处理者不为空,就经过这个callback进行处理。这里就是使用别的方式进行更新UI或者消息的时候,进入这里处理,再也不进入handleMessage  
        if (msg.callback != null) {  
            handleCallback(msg);  
        } else {  
        //默认状况下,走这个分支  
            if (mCallback != null) {  
            //若是全局的callback不为空,就会执行这里,再也不执行handlemessgae  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            // 回传给消息处理者,处理消息,  
            handleMessage(msg);  
        }  
    }

而mCallback 则是咱们初始化构造handler的时候,传入的callback,进行拦截消息处理使用。

总结

咱们能够发现,最终都是进入到了handler的post方法中,经过handler机制来实现。
未完待续....
继续讲解更深刻的细节...

8Looper的建立过程

这里发现有一个sThreadLocal对象,咱们的looper对象就是从这个对象中获取的。这个ThreadLocal对象,其实就是一个与线程相关的对象,保存了线程的相关变量,状态等等。再继续往下看:发现最终就是从这个线程相关的对象中,内部有一个map对象,从里面获取得来。这个地方只是一个单纯的get

    public T get() {  
        Thread t = Thread.currentThread();  
        ThreadLocalMap map = getMap(t);  
        if (map != null) {  
            ThreadLocalMap.Entry e = map.getEntry(this);  
            if (e != null) {  
                @SuppressWarnings("unchecked")  
                T result = (T)e.value;  
                return result;  
            }  
        }  
        return setInitialValue();  
    }

这个地方依然不是咱们想要的答案,咱们想看的是Looper对象是在哪里建立的。

今年金九银十我花一个月的时间收录整理了一套知识体系,若是有想法深刻的系统化的去学习的,能够点击传送门,我会把我收录整理的资料都送给你们,帮助你们更快的进阶。

相关文章
相关标签/搜索