概述
大家对于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生命周期等方法。