Android|Android 消息机制 你了解多少

级别:★★☆☆☆
标签:「Handler」「Android」「消息机制」「内存泄漏」
作者: Zoyp晨
审校: aTaller团队
Handler 原理 Handler基本使用
在Android中使用Handler来切换线程。Android不允许在子线程进行UI操作,也不允许在UI线程(主线程)进行网络请求。在开发中一个非常常见的场景是:子线程中进行网络请求,当网络请求完毕,将数据回调回主线程,这中间用到的角色就是Handler。
  • 为什么Android的主线程不能进行网络请求?
    用户体验不佳,视图展示卡顿
  • 为什么Android的子线程不能进行UI操作?
    Android的UI控件线程不安全
  • 如果要进行子线程切换到主线程的操作,一定要在主线程创建handler吗?
  • 为什么使用Handler有时会IDE会提示可能发生内存泄漏?
相信大家看完本文之后一定可以知道上面几个问题的答案
Handler源码浅析
首先介绍一下Handler体系中的四大组件:
  1. MessageQueue: 是一种元素为Message的单链表的数据结构,线程单例
  2. Looper:MessageQueue是Looper的一个成员变量,线程单例
  3. Message:消息的载体
  4. Handler:处理消息
【Android|Android 消息机制 你了解多少】还有一个很重要的类: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;

Android|Android 消息机制 你了解多少
文章图片
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()
可以简单理解为 ActivithTread这个类是安卓中的应用程序的入口
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机制相关的两行代码,我们挨着分析:
  1. prepareMainLooper
Looper.prepareMainLooper()

为主线程创建了Looper实例,并通过Looper创建了MessageQueue实例,其中MessageQueueLooper的成员变量,Looper通过Threadlocal实现了线程单例,MessagQueue当然也成为线程单例。
  1. 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中,跑在主线程的LoopermessagemessageQueue取出来,通过messagetarget字段对message调用dispatchMessage()进行分发,最终将消息回调给我们刚开始重写的handleMessage方法
关注我们的途径有:
aTaller()
aTaller(掘金)
aTaller(微信公众号)
推荐文章:
Flutte 开发小技巧
Flutter 常用 Widget 介绍
Flutter 图片加载
Flutter 混合栈复用原理
Flutter Platform Channel 使用与源码分析
Flutter Platform View 使用及原理简析
奇舞周刊

    推荐阅读