(点击
上方公众号
,可快速关注)
来源:伯乐在线专栏作者 - PleaseCallMeCoder
链接:http://android.jobbole.com/84957/
点击 → 了解如何加入专栏作者
Android中的消息处理机制概述
大家对于Android中的消息处理机制的用法一定都比较熟悉,至于工作原理估计不少人有研究。就像我们自己写的类我们用起来比较熟悉一样,如果我们熟悉了消息处理机制的具体实现,那么我们用起来肯定也会事半功倍。
博主之前只是稍有涉猎,对其中一些地方也还心存疑虑,比如既然Looper.loop()里是一个死循环,那它会不会很消耗CPU呢?死循环阻塞了线程,那我们其他的事务是如何被处理的呢?Android的UI线程是在哪里被初始化的呢?等等。索性今天就把他们放到一起,说道说道。
Android中线程的分类
有消息时就处理
没有消息时就睡眠
任务一旦执行完成便退出
带有消息队列线程概述
四要素
-
Message(消息)
-
MessageQueue(消息队列)
-
Looper(消息循环)
-
Handler(消息发送和处理)
四要素的交互过程
具体工作过程
-
消息队列的创建
-
消息循环
-
消息的发送最基本的两个API
-
Handler.sendMessage
-
Handler.post
-
消息的处理
基于消息的异步任务接口
-
android.os.HandlerThread
-
android.os.AyncTask
带有消息队列线程的具体实现
ThreadLocal
ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
Looper
用于在指定线程中运行一个消息循环,一旦有新任务则执行,执行完继续等待下一个任务,即变成Looper线程。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.prepare()方法的具体实现
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
();
}
}
//~省略部分无关代码~
}
从中我们可以看到以下几点:
-
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()
/**
* 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
(
"
+
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。
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在未来某个时间点的执行,还可以排列其他线程里执行的操作。
public
class
Handler
{
//~省略部分无关代码~
final
MessageQueue
mQueue
;
final
Looper
mLooper
;
public
Handler
()
{
this
(
null