原文链接:
https://segmentfault.com/a/1190000004866028
https://www.jianshu.com/p/f70ee1765a61
Handler定义
Handler是Android一套非阻塞消息传递机制/异步通信机制
Handler存在意义
在Android中,为了UI操作是线程安全的,规定了只允许UI线程更新 Activity里的UI组件,主线程不能进行耗时操作,否则会阻塞线程,产生ANR异常,所以常常把耗时操作放到其它子线程中进行。但在实际开发中,存在多个线程并发操作UI组件的情况,导致UI操作的线程不安全,所以保证多个线程可并发操作UI组件的同时,线程是安全的。
Handler基本用法
android.os.Handler handler = new Handler(){
@Override
public void handleMessage(final Message msg) {
//这里接受并处理消息
}
};
//发送消息
handler.sendMessage(message);
handler.post(runnable);
通过实例化一个Handler重写handleMessage
方法,然后在需要发消息的时候调用handler对象的send
以及post
系列方法,并且支持发送延时消息,handler发送消息的方法:
sendMessage(Message msg)
sendMessageDelayed(Message msg, long uptimeMillis)
post(Runnable r)
postDelayed(Runnable r, long uptimeMillis)
sendMessageAtTime(Message msg,long when)
sendEmptyMessage(int what)
sendEmptyMessageDelayed(int what, long uptimeMillis)
sendEmptyMessageAtTime(int what, long when)
单看基本用法是不是很简单,那网上说的跟handler相关的MessageQueue
、Looper
呢?并没有看到它们的身影呢?别慌,我们下面从源码一点一点分析。
Handler源码分析
1、首先我们先从Handler发送消息切入,以sendMessage
为例,看下handler是如何把消息入队的。
// Handler类相关方法
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
...
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
...
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
...
//MessageQueue相关方法
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 消息顺序入队
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 消息插入队列,按照when进行比对
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
至此就完成了消息入队操作,值得注意的是,如果消息入队时,队列中有信息,就要进行插队操作,插队按照时间message#when
来进行。
2、说完了消息是如何入队的,接下来我们来讲讲消息是如何出队的,还是从源码的角度进行分析。
// ActivityThread就是我们常说的主线程或UI线程,ActivityThread的main方法是整个APP的入口
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
}
//Looper类相关
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
...
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...
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
...
// Handler target
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
//Handler类相关
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//Message类操作
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
以上部分就完成了消息出队操作:ActivityThread
的main()
调用Looper.loop()
,Looper开始轮询,通过MessageQueue的next()
方法,Looper获取到了message对象之后,调用msg.target.dispatchMessage(msg)
进行消息分发处理。
3、讲完消息的入队出队操作,第三部分要讲的是 Handler,Looper,MessageQueue,Message是如何串联起来的。借用下面图来加深理解。
Handler 常见Q&A
Q:说一说Handler中涉及到哪些类,各自功能是什么 ?
A:handler
、looper
、message
、 MessageQueue
主要是这4个,ThreadLocal
是JDK本身自带类 并不是专门为handler设计的。
handler
是主线程与子线程之间通信的媒介,也是消息的主要处理者。它的作用是将message对象发送到MessageQueue中,也负责Looper分派过来的消息处理。
Looper
是消息队列(Message Queue)与处理者(Handler)的通信媒介,负责从MessageQueue中不断轮询取出队列消息,将消息发送给对应的处理者handler。
MessageQueue
是负责存储handler发送过来的消息,存储特点是 先进先出。
message
是线程间通信的数据单元,即handler接受&处理的对象,存储需要操作的通信信息。
Q:MessageQueue 中的 Message 是有序的吗?排序的依据是什么
A:是有序的,排序的依据是 Message#when
字段,该值是SystemClock#uptimeMillis()
与 delayMillis
之和表示一个相对时间,该值是由 MessageQueue#enqueueMessage(Message, Long)
方法设置的。
Q:子线程中可以创建Handler吗?
A:答案是可以的,前提是需要先创建Looper对象
Looper.prepare();
Handler handler = new Handler();
// ....
// 这一步可别可少了
Looper.loop();
Q:Handler 是如何与 Looper 关联的?
A:我们直接调用无参构造方法时会有一个自动绑定过程
public Handler(@Nullable 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());
}
}
//就是这里 Looper跟Handler进行了绑定
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Q:Looper 是如何与 Thread 关联的?
A:Looper 与 Thread 之间是通过 ThreadLocal 关联的,这个可以看 Looper#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));
}
Q:Handler 有哪些构造方法?
A:Handler的构造方法可以传参数类型为 :Looper
、Handler$Callback
,现在我们就可以算出有多少个公共构造方法了:无参、单Looper、单Callback、Looper和Handler,共4种。
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
Q:looper为什么调用的是Handler的dispatchMessage方法?
A:我们看下dispatchMessage方法
// Handler.java:97
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
从上面的代码不难看出有两个原因:
- 当
msg.callback != null
时会执行handleCallback(msg)
,这表示这个 msg 对象是通过handler#postAtTime(Runnable, long)
相关方法发送的,所以 msg.what和 msg.obj 都是零值,不会交给Handler#handleMessage方法。 - Handler可以接受一个
Callback
参数,类似于 View 里的 OnTouchListener ,会先把事件交给Callback#handleMessage(Message)
处理,如果返回 false 时该消息才会交给Handler#handleMessage(Message)
方法。