Android Handler 消息机制(解惑篇)

概述

大家对于Android中的消息处理机制的用法一定都比较熟悉,至于工作原理估计不少人有研究。就像我们自己写的类我们用起来比较熟悉一样,如果我们熟悉了消息处理机制的具体实现,那么我们用起来肯定也会事半功倍。

博主之前只是稍有涉猎,对其中一些地方也还心存疑虑,比如既然Looper.loop()里是一个死循环,那它会不会很消耗CPU呢?死循环阻塞了线程,那我们其他的事务是如何被处理的呢?Android的UI线程是在哪里被初始化的呢?等等。索性今天就把他们放到一起,说道说道。

Android中线程的分类

带有消息队列,用来执行循环性任务(例如主线程、android.os.HandlerThread)

有消息时就处理

没有消息时就睡眠

没有消息队列,用来执行一次性任务(例如java.lang.Thread)

任务一旦执行完成便退出

带有消息队列线程概述

四要素

Message(消息)

MessageQueue(消息队列)

Looper(消息循环)

Handler(消息发送和处理)

四要素的交互过程

具体工作过程

消息队列的创建

消息循环

消息的发送

最基本的两个API

Handler.sendMessage

带一个Message参数,用来描述消息的内容

Handler.post

带一个Runnable参数,会被转换为一个Message参数

消息的处理

基于消息的异步任务接口

android.os.HandlerThread

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

android.os.AyncTask

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

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

ThreadLocal

ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

Looper

用于在指定线程中运行一个消息循环,一旦有新任务则执行,执行完继续等待下一个任务,即变成Looper线程。Looper类的注释里有这样一个例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<code> 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();
     }
}
</code>

其实核心代码就两行,我们先来看下Looper.prepare()方法的具体实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<code><code> public final class Looper {
 
     private static final String TAG = "Looper" ;
 
     // sThreadLocal.get() will return null unless you've called prepare().
     static final ThreadLocal<looper> sThreadLocal = new ThreadLocal<looper>();
     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();
         }
     }
 
     //~省略部分无关代码~
}
</looper></looper></code></code>

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

prepare()其核心就是将looper对象定义为ThreadLocal 一个Thread只能有一个Looper对象prepare()方法会调用Looper的构造方法,初始化一个消息队列,并且指定当前线程 在调用Looper.loop()方法之前,确保已经调用了prepare(boolean quitAllowed)方法,并且我们可以调用quite方法结束循环

说到初始化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。

接下来再看看Looper.loop()

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<code><code><code> /**
  * 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();
     }
}
</code></code></code>

通过这段代码可知,调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MessageQueue中取出队头的消息(或者说是任务)执行

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

Looper.myLooper()得到当前线程looper对象

getThread()得到looper对象所属线程

quit()方法结束looper循环

这里需要注意的一点是,quit()方法其实调用的是MessageWueue的quite(boolean safe)方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<code><code><code><code> 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);
     }
}
</code></code></code></code>

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

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。

Handler

从MessageQueue的注释中,我们知道添加消息到消息队列是通过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.
*
*/

注释比较简单,这里就不过多翻译了,主要内容是:每一个Handler实例关联了一个单一的ghread和这个thread的messagequeue,当Handler的实例被创建的时候它就被绑定到了创建它的thread。它用来调度message和runnables在未来某个时间点的执行,还可以排列其他线程里执行的操作。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<code><code><code><code><code> 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;
     }
 
     //~省略部分无关代码~
}
</code></code></code></code></code>

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<code><code><code><code><code><code> /**
  * 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) <var>uptimeMillis</var>.
  * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
  * 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);
}
</code></code></code></code></code></code>

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

调用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()方法中的关键代码。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<code><code><code><code><code><code><code> /**
  * 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();
}
</code></code></code></code></code></code></code>

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

思考

为什么要有Handler机制

这个问题可以这么考虑

我们如何在子线程更新UI?——使用Handler机制传递消息到主线程(UI线程) 为什么我们不在子线程更新UI呢?——因为Android是单线程模型 为什么要做成单线程模型呢?——多线程并发访问UI可能会导致UI控件处于不可预期的状态。如果加锁,虽然能解决,但是缺点也很明显:1.锁机制让UI访问逻辑变得复杂;2.加锁导致效率低下。

Handler机制与命令模式

我在之前分享过Android源码中的命令模式,我们仔细分下一下不难看出Handler机制其实是一个非典型的命令模式

接收者:Handler,执行消息处理操作。

调用者:Looper,调用消息的的处理方法。

命令角色:Message,消息类。

客户端:Thread,创建消息并绑定Handler(接受者)。

Android主线程是如何管理子线程消息的

我们知道Android上一个应用的入口,应该是ActivityThread。和普通的Java类一样,入口是一个main方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<code><code><code><code><code><code><code><code> 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" );
}
</code></code></code></code></code></code></code></code>

我们可以看到其实我们在这里初始化了我们主线程(UI)的Looper并且启动它。然后就可以处理子线程和其他组件发来的消息了。

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

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

为什么不会卡死

handler机制是使用pipe来实现的,主线程没有消息处理时会阻塞在管道的读端。

binder线程会往主线程消息队列里添加消息,然后往管道写端写一个字节,这样就能唤醒主线程从管道读端返回,也就是说queue.next()会调用返回。

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

既然是死循环又如何去处理其他事务呢?

答案是通过创建新线程的方式。

我们看到main方法里调用了thread.attach(false),这里便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程。

ActivityThread对应的Handler是一个内部类H,里边包含了启动Activity、处理Activity生命周期等方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值