Android消息机制——Handler
目录
不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!
Handler机制
参考博客:
Android Handler消息机制原理最全解读(持续补充中)
作用
主要是用于不同线程之间的通信。通常在子线程做完耗时操作后,通过handler切换为主线程执行UI操作。
对象
- Handler
消息的发送和处理对象的控制器
- Message
消息的载体
- MessageQueue
存储消息的优先级队列
- Looper
控制消息队列的循环
工作流程
- app启动时,在ActivityThread的main方法中会调用Looper.prepare()方法,初始化looper并绑定主线程。looper对象内部还维护一个MessageQueue。
- 刚开始,通过handler的sendMessage方法发送消息,然后调用MessageQueue.enqueueMessage方法向消息队列中加入消息。
- 主线程调用Looper.loop方法遍历队列,用MessageQueue.next()取出消息。
- 取出的消息不为空,那么调用msg.target.dispatchMessage()来分发消息。目标handler收到消息后,通过handler.handlerMessage()处理消息。
源码分析
- 从子线程的sendMessage方法开始
//新建一个子线程
new Thread(new Runnable() {
@Override
public void run() {
//模拟耗时操作
SystemClock.sleep(3000);
//处理完耗时操作后,发送消息给主线程
handler.sendMessage(new Message());
}
}).start();
Handler中的方法
- 后面会发现,最终都调用了sendMessageDelayed方法
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
//从消息池取出消息,节省内存分配
Message msg = Message.obtain();
//设置消息的标识
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
- 进入sendMessageDelayed方法
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
//推迟时间不合理
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
发现其调用了sendMessageAtTime方法,这个方法将延迟多少时间执行,变成了一个准确的执行时刻。
- 进入sendMessageAtTime方法
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);
}
拿到与handler关联的Looper中的消息队列,然后调用enqueueMessage方法将消息放入消息队列。
- 进入enqueueMessage方法
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//将消息的目标指向handler
//消息与handler关联
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//将消息插入队列
return queue.enqueueMessage(msg, uptimeMillis);
}
将消息与它的目标handler关联,然后执行MessageQueue中的enqueueMessage方法,将消息插入队列。
MessageQueue中的方法
- 基本只需要看一个方法,就是将消息插入消息队列
//向MessageQueue中插入这条消息
//根据when的优先级队列,单链表数据结构
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
//消息是否有目标handler
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
//当前消息是否被使用
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {
//Handler所在线程是否存活
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;
//链表为空,或者时间是0,立刻执行,或者执行时间比表头执行时间早
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
//加在链表头前
msg.next = p;
//指向表头
mMessages = msg;
needWake = mBlocked;
} else {
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在消息队列中加入消息前,需要判断:
- 这条消息是否有关联的handler。
- 这条消息是否已经被使用。
- 这条消息所关联的handler所在的线程是否存活。
当这三个条件都满足的时候,才可以插入消息,否则报错。
插入消息也有两种情况:
- 在表头插入消息
当这个消息队列为空;或者when等于0,意味着立刻执行;或者需要插入的消息的when小于表头消息的when,那么就在表头插入这条消息。
- 在适当位置插入消息
适当位置插入,那么就得先找到这个适当位置。从头开始,死循环遍历消息队列,当到了消息队列的最后或者找到了一个消息的when大于这条消息的when,那么就在这个消息的前面插入即可。
过渡
通过handler发送了消息,并且也通过MessageQueue插入消息到消息队列中了,接下来,就该从队列中取出消息进行处理了,所以需要循环消息队列,需要Looper的loop方法。
Looper中的方法
- Looper的作用就是循环消息队列,看loop方法
//开启Looper
public static void loop() {
//返回当前线程的Looper
final Looper me = myLooper();
//当前线程没有looper,报错
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
//返回和当前线程关联的looper中维护的消息队列
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
//无限循环,死循环
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
textView.setText("对UI进行操作");
}
};
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
//msg.target的dispatchMessage传递消息,即就是handler的这个方法
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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
public static void loop() {
//返回当前线程的Looper
final Looper me = myLooper();
//当前线程没有looper,报错
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
......
//返回和当前线程关联的looper中维护的消息队列
final MessageQueue queue = me.mQueue;
......
//无限循环,死循环
for (;;) {
//不断从队列取消息
Message msg = queue.next();
//到了末尾,队列中没有消息需要处理了
if (msg == null) {
return;
}
......
//msg.target的dispatchMessage传递消息,即就是handler的这个方法
try {
//msg.target就是指向与这个消息关联的handler,所以通过Handler的dispatchMessage方法进行消息传递
msg.target.dispatchMessage(msg);
......
//将处理后需要回收的消息放入消息池缓存
msg.recycleUnchecked();
}
}
回到Handler中
- 通过Handler.dispatchMessage去传递消息
//传递消息
public void dispatchMessage(@NonNull Message msg) {
//若消息的callback不空
if (msg.callback != null) {
//调用这个方法
handleCallback(msg);
} else {
//自己的callback是否空
if (mCallback != null) {
//不空,传给自己的callback
if (mCallback.handleMessage(msg)) {
return;
}
}
//实在不行,调用自己的handleMessage方法
handleMessage(msg);
}
}
从源码中可以看出,若是有Callback接口,那么就会直接回调handleCallback方法。没有的话,只能调用自定义的handleMessage方法去处理消息了。
补充
Handler中的内存泄露问题
出现原因
- 非静态内部类导致的内存泄漏。
- MessageQueue中还有没有处理完的message,message的target即handler对Activity仍然持有引用,会导致内存泄漏。
解决方案
- 使用静态内部类 + 弱引用解决。
- 在销毁Activity时,清除其绑定的looper中维护的MessageQueue中的所有消息。
Handler中的Message对象的获取问题
问题:在使用Handler时,通常是通过Handler.obtainMessage()来获取Message对象的,而其内部调用的是Message.obtain()方法,那么问题来了,为什么不直接new一个Message,而是通过Message的静态方法obtain()来得到的呢?
答:使用obtain创建对象是从消息池中取出Message对象,而new一个新对象,需要为其单独分配一块新的内存,这样可以节省内存的开销。
注意点
-
一个线程只能有一个Looper,内部维护着一个唯一的MessageQueue,但是可以有多个Handler。
-
创建的Looper都会保存在ThreadLocal(线程本地存储区)中。它不是线程,是一个内部存储类。作用是获取当前线程的looper。它可以在不同线程间互不干扰的存储和提供数据。
-
MessageQueue是一个时间优先级队列,但其内部是使用单链表的形式存储,根据时间,将消息插入队列中,表头的先出。
-
通过Looper取出消息,然后通过handler分发消息到handler所在的线程,即进行了不同的线程间通信。
面试问题
-
为什么一个线程只有一个Looper、只有一个MessageQueue?
-
如何获取当前线程的Looper?是怎么实现的?(理解ThreadLocal)
-
是不是任何线程都可以实例化Handler?有没有什么约束条件?
-
Looper.loop是一个死循环,拿不到需要处理的Message就会阻塞,那在UI线程中为什么不会导致ANR?
-
消息循环(死循环)的必要性:
-
looper.looper()阻塞会不会消耗大量的cpu资源
-
Handler.sendMessageDelayed()怎么实现延迟的?结合Looper.loop()循环中,Message=messageQueue.next()和MessageQueue.enqueueMessage()分析。
-
sendMessageDelayed是如何实现延时发送消息的?
-
sendMessageDelayed是通过阻塞来达到了延时发送消息的结果,那么会不会阻塞新添加的Message?
下一篇: 常用的正则匹配