Android源码分析之消息机制Handler

作者: sososeen09 | 来源:发表于2016-06-23 17:51 被阅读274次

    1 前言

    Handler在Android开发中使用的比较多,通常Handler是在主线程中创建,子线程拿到这个Handler向主线程中发送消息。

    那么如果需要主线程中向子线程中发送消息呢?

    本文只是提出这个问题,并不在此提供实现这种场景的例子,写这篇文章的目的主要是理解Handler的运行原理,以便更好、更灵活的运用Handler。

    2 Android消息机制简介

    1. Android的消息机制主要是指Handler的运行机制,Handler的运行依赖于MessageQueue和Looper,当然,既然是消息机制,通常也需要用到Message。
    2. Handler、Looper、MessageQueue和Message的工作原理就像是生产线,Looper是发动机,MessageQueue就是传送带,Handler是工人,Message就是待处理的产品

    3 Handler的工作流程

    一个应用的启动需要有main方法作为启动的入口,在Android中这个main方法在ActivityThread类中,查看这个类的main方法可以看到:

    public static final void main(String[] args) {
            //省略无关代码
            ...
            //主线程中调用Looper.prepareMainLooper()方法创建Looper
            Looper.prepareMainLooper();
            if (sMainThreadHandler == null) {
                sMainThreadHandler = new Handler();
            }
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
        //主线程中调用Looper.loop(),开始轮询,取消息
            Looper.loop();
    
            if (Process.supportsProcesses()) {
                throw new RuntimeException("Main thread loop unexpectedly exited");
            }
    
           ...
        }
    }
    

    3.1 Looper轮询器

    在ActivityThread的main方法中首先调用Looper.prepareMainLooper()方法,我们来看一下,这个方法中做了哪些工作:

    在Looper类中
    public static final void prepareMainLooper() {
         prepare();
         setMainLooper(myLooper());
         if (Process.supportsProcesses()) {
            myLooper().mQueue.mQuitAllowed = false;
         }
    }
    

    该方法中首先调用本类中的prepare()方法,这个方法的主要作用是创建Looper对象,并且把该对象绑定到当前线程中,在这里既然是主线程调用的,那么该Looper对象也就是在主线程当中

    在Looper类中
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //把Looper绑定到当前线程
        sThreadLocal.set(new Looper());
    }
    

    在Looper的构造方法中,我们可以看到:

    private Looper() {
        //创建消息队列
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }
    

    首先,Looper这个类的构造方法是私有的,也就是说不允许外界直接new出来Looper对象。在创建Looper的时候,同时创建了一个消息队列MessageQueue。

    所以请注意,消息队列MessageQueue是在Looper创建的时候,同时创建的

    通过调用Looper.loop()方法,开始轮询消息:

       public static final void loop() {
            Looper me = myLooper();
            //通过拿到Looper对象,取获取与之对应的消息队列MessageQueue
            MessageQueue queue = me.mQueue;
           ...
            //通过死循环方式去取消息
            while (true) {
                //调用MessageQueue的next()方法取消息,这个过程也是死循环
                Message msg = queue.next(); // might block
                
                if (msg != null) {
                    ...
                    //取到消息之后,交给发送该消息的Handler取处理消息
                    msg.target.dispatchMessage(msg);
                   ...
                    //回收消息,在Message中维护的有一个消息池
                    msg.recycle();
                }
            }
        }
    

    通过上述简单的叙述,我们可以明白,在Activity启动的时候创建的一个与主线程相关的Looper和对应的消息队列MessageQueue,并且通过消息队列通过阻塞的方式去取消息。

    3.2 Handler的工作过程

    Handler的工作过程主要包含发送消息和处理消息。Handler的构造方法有多个,他们的共同点是均会获取当前线程的Looper对象和消息队列MessageQueue对象

    //Handler的构造方法1
    public Handler() {
        ...
        //获取当前线程的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 = null;
    }
    
    //Handler的构造方法2
    public Handler(Callback callback) {
       ...
        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;
    }
    
    //Handler的构造方法3
    public Handler(Looper looper) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = null;
    }
    
    //Handler的构造方法4
    public Handler(Looper looper, Callback callback) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
    }
    

    发送消息可以通过一系列send方法,也可以通过一系列post方法,不过post方法最终还是通过send方法去实现。
    用send方法,最终也会调用一个方法如下:

    //在Handler中
    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            //调用msg.target的方法把Message和发送它的Handler绑定,所以Looper能够把正确的Message交给发送它的Handler处理
            msg.target = this;
            //调用MessageQueue的enqueueMessage方法把消息加入消息队列
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }
    

    可以看到,Handler发送消息的过程就是向消息队列中插入一条消息。前面已经讲到,MessageQueue调用next方法去轮询消息,那么当MessageQueue拿到消息之后,把消息传递给Looper,最终交给Handler去处理,即dispatchMessage方法会被调用。此时,Handler开始处理消息。值得一提的是,在消息队列中可能有不同Handler发送的多个消息,通过在发送消息的时候把Message和发送它的Handler绑定,Looper就会把消息正确的交给发送它的Handler来处理。dispatchMessage方法如下:

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

    Handler处理消息的过程如下:

    1. 查看Message的callback是否为null,不为null的话就通过handleCallback(msg)方法处理消息。这里的callback实际上就是一个Runnable对象,如果以post方式去发送消息,最终就会调用handleCallback(msg)方法去处理,这个方法内容为:

       private final void handleCallback(Message message) {
           message.callback.run();
       }
      
    2. 检查mCallBack是否为null,如果不为null就调用mCallBack的handleMessage(msg)方法。这个mCallBack是CallBack接口的子类对象,前面已经说过Handler的构造方法中有两个可以用到这个CallBack。

    3. 如果mCallBack为null,最终就会调用Handler的handleMessage(msg)方法,这个方法通常是在创建Handler时被使用者重写的方法。

    需要说明的是:在主线程当前我们创建Handler时并没有自己创建Looper对象,这是因为主线程已经为我们创建好了;如果要在子线程当前创建Handler,一定要在之前创建Looper对象,即调用Looper.prepare()方法。

    3.3 MessageQueue消息队列

    前面的内容已经讲了很多关于MessageQueue的东西,这里也就不再赘述,MessageQueue主要包含两个操作:插入消息(enqueueMessage方法)和读取消息(next方法)。读取的过程也伴随着删除操作。MessageQueue的的内部实际上是通过一个单链表的数据结构来维护消息列表,这主要也是因为单链表在插入和删除上比较有优势。

       final boolean enqueueMessage(Message msg, long when) {
        ...
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                // 当前发送的message需要马上被处理调,needWake唤醒状态置true
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
             } else {
                  // 当前发送的message被排队到其他message的后面,needWake唤醒状态置为false
                    Message prev = null;
                    while (p != null && p.when <= when) {
                        prev = p;
                        p = p.next;
                    }
                    msg.next = prev.next;
                    prev.next = msg;
                    needWake = false; // still waiting on head, no need to wake up
                }
        }
        // 是否唤醒主线程
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }
    

    enqueueMessage的主要操作其实就是单链表的插入操作,根据时间看当前发送的Message是否需要马上处理。这个enqueueMessage方法是Handler发送消息的时候调用。

    下面来看next方法:

    final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
    
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);
    
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }
    
            ...
    }
    

    可以看到,next方法返回一个Message对象。next方法中也是采用阻塞的方式去获取消息队列中的消息,一旦有消息立即返回并且将它从单链表中移除。如果没有消息就一直阻塞。前面已经提到,这个取消息的next方法是在Looper的loop()方法中调用。

    3.4 Message消息载体

    Message只有一个无参构造方法,但是Message有多个obtain静态方法来返回Message对象。

    采用哪种方式创建Message对象都可以,但是建议采用obtain方法来创建。这是因为Message通过在内部构建一个链表来维护一个被回收的Message对象池。当用户调用obtain方法时会优先从池中获取,如果池中没有则创建新的Message对象。同时在使用完毕之后,进入池中以便于复用。这个在Looper.loop()方法可以看到一点端倪,在使用完毕时候调用了Message的recycle()方法。
    下面是obtain方法创建Message对象的流程:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    

    4 小结

    • 在应用启动时,会开启一个主线程(即UI线程),会创建一个Looper对象,并把该对象绑定到主线程中。Looper对象被封装在ThreadLocal中,使得不同线程间的Looper不能共享
    • 创建Looper时同时创建了MessageQueue消息队列对象
    • Handler通过send方法或者post方法,把消息加入消息队列MessageQueue中
    • 主线程中调用Looper的loop()方法,会开启消息循环,不断的从消息队列中取出消息
    • Looper拿到消息之后调用Handler的dispatchMessage方法来处理消息

    相关文章

      网友评论

      本文标题:Android源码分析之消息机制Handler

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