Android|Android 消息机制 你了解多少
级别:★★☆☆☆Handler 原理 Handler基本使用
标签:「Handler」「Android」「消息机制」「内存泄漏」
作者: Zoyp晨
审校: aTaller团队
在Android中使用Handler来切换线程。Android不允许在子线程进行UI操作,也不允许在UI线程(主线程)进行网络请求。在开发中一个非常常见的场景是:子线程中进行网络请求,当网络请求完毕,将数据回调回主线程,这中间用到的角色就是Handler。
- 为什么Android的主线程不能进行网络请求?
用户体验不佳,视图展示卡顿 - 为什么Android的子线程不能进行UI操作?
Android的UI控件线程不安全 - 如果要进行子线程切换到主线程的操作,一定要在主线程创建handler吗?
否 - 为什么使用Handler有时会IDE会提示可能发生内存泄漏?
Handler源码浅析
首先介绍一下Handler体系中的四大组件:
- MessageQueue: 是一种元素为Message的单链表的数据结构,线程单例
- Looper:MessageQueue是Looper的一个成员变量,线程单例
- Message:消息的载体
- Handler:处理消息
ThreadLocal
,Looper就是凭借着TheadLocal这个类的能力来实现线程单例。Handler的发送消息的流程 先从最熟悉的Handler.sengMessage(Message msg)方法看起:
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);
}
上面三个方法的调用链依次为
sendMessage -> sendMessageDelayed -> sendMessageAtTime
sendMessage方法去调用sendMessageDelayed方法,将delaytime设置为0,让Handler立即从Message队列中取出这条消息.sendMessageAtTime方法将相对时间转换为绝对时间。最后通过enqueueMessage方法来将消息入队,这里的队列就是线程单例的MessageQueue。
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);
}
为什么使用匿名内部类来创建Handler的方法会有内存泄漏的风险? 就是因为上面那一句话
msg.target = this;
构成上面的引用链的原因:
文章图片
Handler引用链
- 匿名内部类实现的方法隐式的持有外部类的引用,通常也就是Activity
- Message的targer字段引用着Handler
- 发送的是一条延时消息,message在Messagequeue中一段事件得不到处理
boolean enqueueMessage(Message msg, long when) {synchronized (this) {msg.when = when;
Message p = mMessages;
boolean needWake;
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;
prev.next = msg;
}if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage方法按照延迟时间的从小到大顺序将 Message插入队列中的适当。
到这里MessageQueue的入队操作就分析完毕了
主线程开启Looper循环的流程 app的启动流程
- Zygote进程通过轮训,查询当前是否有启动app的请求消息。
- 当我们从Laucher(桌面)启动一个app时,SystemServer进程发送消息到Zygote。
- Zygote通过fork为app创建子进程
- 子进程通过反射调用
android.app.ActivityThread.main()
main函数
public static void main(String[] args) {
...Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
...
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
其中与Handler机制相关的两行代码,我们挨着分析:
- prepareMainLooper
Looper.prepareMainLooper()
为主线程创建了Looper实例,并通过
Looper
创建了MessageQueue
实例,其中MessageQueue
是Looper
的成员变量,Looper通过Threadlocal
实现了线程单例,MessagQueue
当然也成为线程单例。- loop
为主线程开启消息循环
Looper.loop();
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;
}
...
try {
//这里通过msg.target将取出的消息回调给发送消息的那个handler
msg.target.dispatchMessage(msg);
...
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}...
msg.recycleUnchecked();
}
}
通过
msg.target
字段,将取出的消息回调给我们在主线程创建的Handler,这就是为什么前面在发送消息的流程中,我们需要Message去持有Hadnler的引用,这里就将逻辑转移到了Handler的dispatchMessage方法中,开启分发消息的流程。Observer 还有一个平常没有注意到的东西:Observer
public static void setObserver(@Nullable Observer observer) {
sObserver = observer;
}
public interface Observer {Object messageDispatchStarting();
void messageDispatched(Object token, Message msg);
void dispatchingThrewException(Object token, Message msg, Exception exception);
}
我们可以通过looper注册观察者,来观察到从looper中取出后、分发前的这个时机的Message,对此进行一些打印日志或者一些其他的操作
Handler分发消息的流程
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
绕了一大圈,终于看到
handleMessage
这个方法了。dispatchMessage方法中对与message的分发有三种结果,分别是 message携带的callback字段
, handler本身的callback字段
,handleMessage方法
,其中hadnleMessage方法应该大家平常最常用。而msg的callback字段,和handler的callback字段也是可以对消息进行处理,读者有兴趣的话可以自己去敲代码实验一下如何用前两种callback的方法对消息进行处理。总结
我们通过对源码的分析,基于
Message
的传递路线绕了一圈,终于有头有尾的走了一遍下来。我们再来回顾一下:通过在主线程创建了Handler
,然后在子线程中通过主线程创建的Handler
实例将携带信息的message
发送到handler
绑定的messagequeue
中,跑在主线程的Looper
将message
从messageQueue
取出来,通过message
的target
字段对message
调用dispatchMessage()
进行分发,最终将消息回调给我们刚开始重写的handleMessage
方法关注我们的途径有:
aTaller()
aTaller(掘金)
aTaller(微信公众号)
推荐文章:
Flutte 开发小技巧
Flutter 常用 Widget 介绍
Flutter 图片加载
Flutter 混合栈复用原理
Flutter Platform Channel 使用与源码分析
Flutter Platform View 使用及原理简析
奇舞周刊
推荐阅读
- android第三方框架(五)ButterKnife
- 危险也是机会
- python学习之|python学习之 实现QQ自动发送消息
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- android|android studio中ndk的使用
- Android事件传递源码分析
- RxJava|RxJava 在Android项目中的使用(一)
- Android7.0|Android7.0 第三方应用无法访问私有库
- 深入理解|深入理解 Android 9.0 Crash 机制(二)