1. 消息机制概述

Android中的消息机制主要指的是Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper、Message的支撑,下文会逐一分析。

2. 为什么需要消息机制

Android中的消息机制主要是为了满足线程间通信而设计的,最重要的应用场景应该在于更新UI

Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,那么程序就会抛出异常


系统为什么不允许在自线程中访问UI呢?这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。


那为什么不对UI控件的访问加上锁机制呢?缺点有两个:

  1. 首先加上锁机制会让UI访问的逻辑变得复杂;
  2. 其次锁机制会降低访问UI的效率,因为锁机制会阻塞某些现成的执行

鉴于这两个缺点,最简单且最高效的方法就是采用单线程模型来处理UI操作,对于开发者来说也不是很麻烦,只是需要通过Handler切换下UI的访问执行线程即可

3. Android中线程的分类

  1. 带有消息队列,用来执行循环型任务(例如主线程ActivityThreadAndroid.os.HandlerThread)有消息时就处理,没有消息时就睡眠
  2. 没有消息队列,用来执行一次性任务(例如java.lang.Thread
    任务一旦执行完成便退出

4. 带有消息队列的线程具体实现

主要涉及4个方面

  1. Message(消息)
  2. MessageQueue(消息队列)
  3. Looper(消息循环)
  4. Handler (消息发送和处理)

4.1 消息队列

说到MessageQueue,我们来看下它是干什么的

/**
* Low-level class holding the list of messages to be dispatched by a
* {@link Looper}. Messages are not added directly to a MessageQueue,
* but rather through {@link Handler} objects associated with the Looper.
*
*You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/

它是一个低等级的持有Messages集合的类,被Looper分发。Messages并不是直接加到MessageQueue的,而是通过Handler对象和Looper关联到一起。我们可以通过Looper.myQueue()方法来检索当前线程的MessageQueue。

4.2 Message

在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。我们看下这个类的注释

/**
*
* Defines a message containing a description and arbitrary data object that can be
* sent to a {@link Handler}. This object contains two extra int fields and an
* extra object field that allow you to not do allocations in many cases.
*
* While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.
*/

这个类定义了一个包含描述和一个任意类型对象的对象,它可以被发送给Handler。
从注释里我们还可以了解到以下几点:

  • 尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。
  • 如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
  • 用message.what来标识信息,以便用不同方式处理message

4.3 Looper

/*
* This class contains the code required to set up and manage an event loop
* based on MessageQueue. APIs that affect the state of the queue should be
* defined on MessageQueue or Handler rather than on Looper itself. For example,
* idle handlers and sync barriers are defined on the queue whereas preparing the
* thread, looping, and quitting are defined on the looper.
*/

这个类是基于消息队列用来设置和管理事件循环的代码,对队列的改变应该在MessageQueue或Handler上定义,而不是在Looper本身定义,例如空间处理和同步障碍应该在队列上定义,然而准备线程、循环和退出在Looper本身定义,

4.4 Handler

/**
* A Handler allows you to send and process {@link Message} and Runnable
* objects associated with a thread’s {@link MessageQueue}. Each Handler
* instance is associated with a single thread and that thread’s message
* queue. When you create a new Handler, it is bound to the thread /
* message queue of the thread that is creating it – from that point on,
* it will deliver messages and runnables to that message queue and execute
* them as they come out of the message queue.
*
*

There are two main uses for a Handler: (1) to schedule messages and
* runnables to be executed as some point in the future; and (2) to enqueue
* an action to be performed on a different thread than your own.
*
*

Scheduling messages is accomplished with the
* {@link #post}, {@link #postAtTime(Runnable, long)},
* {@link #postDelayed}, {@link #sendEmptyMessage},
* {@link #sendMessage}, {@link #sendMessageAtTime}, and
* {@link #sendMessageDelayed} methods. The post versions allow
* you to enqueue Runnable objects to be called by the message queue when
* they are received; the sendMessage versions allow you to enqueue
* a {@link Message} object containing a bundle of data that will be
* processed by the Handler’s {@link #handleMessage} method (requiring that
* you implement a subclass of Handler).
*
*

When posting or sending to a Handler, you can either
* allow the item to be processed as soon as the message queue is ready
* to do so, or specify a delay before it gets processed or absolute time for
* it to be processed. The latter two allow you to implement timeouts,
* ticks, and other timing-based behavior.
*
*

When a
* process is created for your application, its main thread is dedicated to
* running a message queue that takes care of managing the top-level
* application objects (activities, broadcast receivers, etc) and any windows
* they create. You can create your own threads, and communicate back with
* the main application thread through a Handler. This is done by calling
* the same post or sendMessage methods as before, but from
* your new thread. The given Runnable or Message will then be scheduled
* in the Handler’s message queue and processed when appropriate.
*/

这个有点长,简单概括下:每一个Handler实例关联了一个单一的ghread和这个thread的messagequeue,当Handler的实例被创建的时候它就被绑定到了创建它的thread。它用来调度message和runnables在未来某个时间点的执行,还可以排列其他线程里执行的操作。

Handler主要用的来管理某个线程(也可能是进程)的消息队列。
比如处理主线程的消息队列,包含消息的发送和接收过程。
消息的发送可以通过Post的一系列方法和Sende的一系列方法来实现。
而post的一系列方法最终是通过send的一系列方法来实现的。这样就可以将一些耗时任务放到其他线程之中,待任务完成之后就往主线程的消息队列中添加一个消息,这样Handler的Callback,即handleMessage就会被调用。但是Handler并不是线程安全的,因此建议将Handler作为一个静态内部类。
所以Handler只是处理消息,耗时任务放在其他线程。

4.5 四元素的交互过程

具体工作过程

  • 消息队列的创建
  • 消息循环
  • 消息的发送最基本的两个API
    • 带一个Runnable参数,会被转换为一个Message参数
    • 带一个Message参数,用来描述消息的内容
    • Handler.sendMessage
    • Handler.post
  • 消息的处理

基于消息的异步任务接口

  • android.os.HandlerThread

    适合用来处于不需要更新UI的后台任务

  • android.os.AyncTask

    适合用来处于需要更新UI的后台任务


4.5.1 Looper与消息队列的创建

首先确认当前线程是否具有Looper(主线程默认具有Looper)如果没有则创建
我们知道Android上一个应用的入口,应该是ActivityThread。和普通的Java类一样,入口是一个main方法。创建主线程源码示例:

public static void main(String[] args) {    //~省略部分无关代码~    //创建Looper和MessageQueue对象,用于处理主线程的消息    Looper.prepareMainLooper();    //创建ActivityThread对象    ActivityThread thread = new ActivityThread();    //建立Binder通道 (创建新线程)    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);    //消息循环运行    Looper.loop();    throw new RuntimeException("Main thread loop unexpectedly exited");}

我们可以看到主方法首先通过Looper.prepareMainLooper()初始化了我们主线程(UI)的Looper并且启动它。然后就可以处理子线程和其他组件发来的消息了


如果不是主线程的两线程进行通信,可以通过以下方式来创建

class LooperThread extends Thread {    public Handler mHandler;    public void run() {        //将当前线程初始化为Looper线程        Looper.prepare();        // ...其他处理,如实例化handler        mHandler = new Handler() {            public void handleMessage(Message msg) {                // process incoming messages here            }        };        // 开始循环处理消息队列        Looper.loop();    }}

Looper源码如下:

public final class Looper {    private static final String TAG = "Looper";    // sThreadLocal.get() will return null unless you've called prepare().    static final ThreadLocal sThreadLocal = new ThreadLocal();    private static Looper sMainLooper;  // guarded by Looper.class    //Looper内的消息队列    final MessageQueue mQueue;    // 当前线程    final Thread mThread;    private Printer mLogging;    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }     /** Initialize the current thread as a looper.      * This gives you a chance to create handlers that then reference      * this looper, before actually starting the loop. Be sure to call      * {@link #loop()} after calling this method, and end it by calling      * {@link #quit()}.      */    public static void prepare() {        prepare(true);    }    private static void prepare(boolean quitAllowed) {        //试图在有Looper的线程中再次创建Looper将抛出异常        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper(quitAllowed));    }    /**     * Initialize the current thread as a looper, marking it as an     * application's main looper. The main looper for your application     * is created by the Android environment, so you should never need     * to call this function yourself.  See also: {@link #prepare()}     */    public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }    //~省略部分无关代码~}

从中我们可以看到以下几点:

  1. prepare()其核心就是将looper对象定义为ThreadLocal
  2. 一个Thread只能有一个Looper对象
  3. prepare()方法会调用Looper的构造方法,初始化一个消息队列,并且指定当前线程

ThreadLocal并不是一个Thread,而是Thread的局部变量。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本。
所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

在调用Looper.loop()方法之前,确保已经调用了prepare(boolean quitAllowed)方法,并且我们可以调用quite方法结束循环


接下来再看看Looper.loop()

/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */public static void loop() {    //得到当前线程Looper    final Looper me = myLooper();    if (me == null) {        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");    }    //得到当前looper的MessageQueue    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        Printer logging = me.mLogging;        if (logging != null) {            logging.println(">>>>> Dispatching to " + msg.target + " " +                    msg.callback + ": " + msg.what);        }        //将真正的处理工作交给message的target,即handler        msg.target.dispatchMessage(msg); 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方法后,Looper线程就开始真正工作了,它不断从自己的MessageQueue中取出队头的消息(或者说是任务)执行。

除了prepare()和loop()方法,Looper类还有一些比较有用的方法,比如

  • Looper.myLooper()得到当前线程looper对象
  • getThread()得到looper对象所属线程
  • quit()方法结束looper循环这里需要注意的一点是,quit()方法其实 调用的是MessageWueue的quite(boolean safe)方法。
void quit(boolean safe) {    if (!mQuitAllowed) {        throw new IllegalStateException("Main thread not allowed to quit.");    }    synchronized (this) {        if (mQuitting) {            return;        }        mQuitting = true;        if (safe) {            removeAllFutureMessagesLocked();        } else {            removeAllMessagesLocked();        }        // We can assume mPtr != 0 because mQuitting was previously false.        nativeWake(mPtr);    }}

我们看到其实主线程是不能调用这个方法退出消息队列的。至于mQuitAllowed参数是在Looper初始化的时候初始化的,主线程初始化调用的是Looper.prepareMainLooper()方法,这个方法把参数设置为false。


4.5.2 消息的发送和处理(Handler)

从MessageQueue的注释中,我们知道添加消息到消息队列是通过Handler来操作的。我们通过源码来看下具体是怎么实现的

public class Handler {    //~省略部分无关代码~    final MessageQueue mQueue;    final Looper mLooper;    public Handler() {        this(null, false);    }    public Handler(Looper looper) {        this(looper, null, 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());            }        }        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;    }    //~省略部分无关代码~}

先看构造方法,其实里边的重点是初始化了两个变量,把关联looper的MessageQueue作为自己的MessageQueue,因此它的消息将发送到关联looper的MessageQueue上。

有了handler之后,我们就可以使用Handler提供的post和send系列方法向MessageQueue上发送消息了。其实post发出的Runnable对象最后都被封装成message对象


接下来我们看一下handler是如何发送消息的

/** * Causes the Runnable r to be added to the message queue. * The runnable will be run on the thread to which this handler is  * attached.  *   * @param r The Runnable that will be executed. *  * @return Returns true if the Runnable was successfully placed in to the  *         message queue.  Returns false on failure, usually because the *         looper processing the message queue is exiting. */public final boolean post(Runnable r){   return  sendMessageDelayed(getPostMessage(r), 0);}/** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. *   * @return Returns true if the message was successfully placed in to the  *         message queue.  Returns false on failure, usually because the *         looper processing the message queue is exiting.  Note that a *         result of true does not mean the message will be processed -- if *         the looper is quit before the delivery time of the message *         occurs then the message will be dropped. */public final boolean sendMessageDelayed(Message msg, long delayMillis){    if (delayMillis < 0) {        delayMillis = 0;    }    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) uptimeMillis. * The time-base is {@link android.os.SystemClock#uptimeMillis}. * Time spent in deep sleep will add an additional delay to execution. * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. *  * @param uptimeMillis The absolute time at which the message should be *         delivered, using the *         {@link android.os.SystemClock#uptimeMillis} time-base. *          * @return Returns true if the message was successfully placed in to the  *         message queue.  Returns false on failure, usually because the *         looper processing the message queue is exiting.  Note that a *         result of true does not mean the message will be processed -- if *         the looper is quit before the delivery time of the message *         occurs then the message will be dropped. */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);}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    msg.target = this;    if (mAsynchronous) {        msg.setAsynchronous(true);    }    return queue.enqueueMessage(msg, uptimeMillis);}

这里我们只列出了一种调用关系,其他调用关系大同小异,我们来分析一下

  • 调用getPostMessage(r),把runnable对象添加到一个Message对象中。
  • sendMessageDelayed(getPostMessage(r), 0),基本没做什么操作,又继续调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在这个方法里拿到创建这个Handler对象的线程持有的MessageQueue。
  • 调用enqueueMessage(queue, msg, uptimeMillis)方法,给msg对象的target变量赋值为当前的Handler对象,然后放入到MessageQueue。

那发送消息说完了,那我们的消息是怎样被处理的呢?

我们看到message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码。

/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */public interface Callback {    public boolean handleMessage(Message msg);}/** * Subclasses must implement this to receive messages. */public void handleMessage(Message msg) {}/** * Handle system messages here. */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();}

我们看到这里最终又调用到了我们重写的handleMessage(Message msg)方法来做处理子线程发来的消息或者调用handleCallback(Message message)去执行我们子线程中定义并传过来的操作


为什么主线程不会因为Looper.loop()里的死循环卡死或者不能处理其他事务

这里涉及到的东西比较多,概括的理解是这样的

  • handler机制是使用pipe来实现的,主线程没有消息处理时会阻塞在管道的读端。
  • binder线程会往主线程消息队列里添加消息,然后往管道写端写一个字节,这样就能唤醒主线程从管道读端返回,也就是说queue.next()会调用返回。

主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

既然是死循环又如何去处理其他事务呢?答案是通过创建新线程的方式。
我们看到main方法里调用了thread.attach(false),这里便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程。

参考资料:

http://android.jobbole.com/84957/

更多相关文章

  1. 【转】有关Android线程的学习
  2. Android基础笔记(五)-网络编程
  3. Android线程
  4. Android必备知识(五)多线程及AsyncTask
  5. 浅析Android单线程模型
  6. Android(安卓)加载模型
  7. Android(安卓)应用程序消息处理机制(Looper、Handler)分析
  8. 浅析Android线程模型
  9. 深入剖析Android消息机制

随机推荐

  1. android之bitmap详解
  2. Android(安卓)解决Could not find com.an
  3. Mac上Android(安卓)studio环境搭建
  4. android OS Service
  5. 【android动画】关于android:persistentD
  6. android Service onStartCommand intent
  7. Android初级教程人品计算器
  8. Android保持屏幕常亮的方法
  9. android
  10. js判断运行平台