Android进阶知识树——Android消息队列

一、概述

在安卓程序启动时,会默认在主线程中 运行程序,那若是执行一些耗时的操做则UI就会处于阻塞状态,出现界面卡顿的现象,再者用户的多种操做,系统是如何作到一一处理的,系统又是如何管理这些任务的,答案这就是今天的主题Android的消息机制;bash

  • Android处理消息的方式——handler、Looper 和 MessageQueue
  1. Handler :负责将任务添加到队列,执行结束后在主线程执行UI操做
  2. Looper :未绑定的线程开启循环消息队列,并获取消息
  3. MessageQueue :任务队列,保存发送的消息

二、源码分析

对于Handler的使用方法此处不作介绍,在使用是只要注意避免内存泄漏需建立静态Handler,此处从执行过程和原理的角度简单分析Handler的源码,首先你们都知道在主线程中能够直接使用Handler,而在子线程中不能够,有没有想过为何呢?如今就先看看主线程究竟有什么不一样吧,从头看安卓的入口方法ActivityThread.main()async

​
public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
      ...
        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"));
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

​复制代码
  • 上述代码看出在程序启动后,执行三个步骤:
  1. 调用Looper.prepareMainLooper();建立消息循环Looper
  2. 使用 thread.getHandler(); 获取UI线程的Handler
  3. 使用 Looper.loop()开启消息循环

Handler与消息队列关联,而消息队列被封装在Looper中,每一个Looper关联一个线程,因此Handler的消息大体为handler做为一个消息处理器,将消息传递到消息队列中,线程中的Looper逐个取出消息并执行,此处的线程即为UI线程,ide

  • Handler最基本的用法是建立Handler 发送Message到队列,如今看看建立Handler时都作了哪些事
​
 public Handler(Callback callback, boolean async) {
        
        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;
    }

​复制代码
  1. 调用Looper.myLooper():获取Looper对象,若是为空则抛出异常,而后调用lopper.mQuene开启消息队列
  • 查看myLooper()方法
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }复制代码
  1. 在myLooper中看出,Looper从sThreadLocal中获取,那何时设置进去的呢?另外从这也能够看出来,只要sThreadLocal没有Looper的实例就会抛出异常,sThreadLocal就是执行的线程,换句话说只要Handler所在的线程中没有Looper的实例就会异常;
  • 接下来看看UI线程开始时建立的Looper对象的方法prepareMainLooper()
​public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }复制代码
  • prepareMainLooper() 调用了prepare()方法:
​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));
    }复制代码
  1. 看到了 sThreadLocal.set(new Looper(quitAllowed)),此处建立Looper对象设置进sThreadLocal中,是否是明白了上面的sThreadLocal。get()获取的Looper是哪里的了。

到这里咱们介绍了Handler 和 Looper的建立,以及各自对线程的关联,下面查看如何启动消息循环获取任务并执行,在入口方法的最后调用了Loop.loop()方法,下面查看Looper.loop()方法:oop

public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }
...
 msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}复制代码
  1. 调用myLooper()获取Looper的对象,获取消息队列后调用next()依次取出其中的Message,注意这里是个死循环
  2. 调用Messagemsg.target.dispatchMessage(msg)执行方法
  • 查看Message的源码便知target实际是Handler的对象,说明绕了一圈最后仍是Handler的dispatchMessage(msg)处理逻辑
Handler target;
Runnable callback;
Message next;复制代码
  • Handler的dispatchMessage(msg)方法
​public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
private static void handleCallback(Message message) {
        message.callback.run();
    }复制代码

总结一下:looper从MessageQuene中依次取出Message,而后调用Msg绑定的Handler的dispatchMessage(msg)处理,dispatchMessage()中依次判断执行:源码分析

  1. 先判断Message的Runable对象callback是否为null,若不为空使用handleCallback()即调用Runnable,run()执行
  2. 若callback为空判断mCallBack,若mCallBack不为空则调用mCallBack.handleMessage()处理
  3. 若是上面二者都为null,则执行handleMessage(),此处即为建立Handler()重写的handlerMessage()

到此Android中Handler的消息处理,从建立Handler、Looper和MessageQueue到消息的传递和事件的处理都介绍完了,下面咱们看看使用时如何发送消息到MessageQueue以及上述的callback、mCallBack什么状况下为空?post

  • 建立的Handler后发送消息方法:sendMessage(Message msg)、post(Runnable r)
  1. sendMessage()
public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        
        return enqueueMessage(queue, msg, uptimeMillis);
    }

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;   // 此处设置target为Handler对象
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }复制代码

上面的执行过程代码从上倒下一目了然,在enqueueMessage()中设置了Message.target即为调用方法的Handler,因此在调用dispatchMessage()时,会调用Handler的handleMessage()处理ui

  • post(Runnable r)
public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;   // 设置Message的callback为r
        return m;
    }复制代码
  1. 调用getPostMessage()设置Message中的callback,因此callback就是发送的runnable,其他方法和sendMessage()一致。

Handler消息机制的原理和过程介绍完毕了,此时知道为何不能在子线程建立handler吗?抛出的异常在上述代码中也出现了this

mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }复制代码

当Looper.myLooper()取出为空时抛出异常,而myLooper()中时从sThreadLocal.get()中获取,即此时sThreadLocal中没有设置Looper对象,因此也就没有后面的队列等操做,从上面知道建立并设置Looper对象是在prepare()方法中,因此要在子线程中使用Handler要先调用Looper.prepare()建立Looper对象,后调用Looper.loop()开启消息循环。spa

相关文章
相关标签/搜索