浅谈Handler消息处理机制

2,032 阅读12分钟

1.什么是Handler

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

简而言之:

Handler就是Android系统提供给我们,用来更新UI的一套机制,也是一套消息处理机制。通过Handler,可以用来发送消息,处理消息。如果不遵循这样的机制,就没有办法更新UI,会抛出异常。

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

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

2.Message: 消息

3.MessageQueue: 消息存放的队列

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

5.线程: 当前是在哪个线程

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

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

eg: 我们都知道读书的时候,我们经常需要向老师请假之类。

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

当然,这只是比喻。我们通过这个比喻,来比较生动形象的去理解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);
                }
            }
          
        }
    }

可以看到:

    1. 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对象是在哪里创建的。