Handler源码分析

本文详细分析了Handler的源码,包括Looper、MessageQueue、Handler的创建过程,以及信息的发送、取出和处理机制。在Android应用启动时,ActivityThread的main方法创建Looper和MessageQueue,通过Looper.loop()的死循环来不断处理Message,实现跨线程通信。Handler在创建时获取当前线程的Looper,并通过enqueueMessage()将Message放入MessageQueue。同时,文中探讨了如何在子线程中停止Looper以及主线程Looper为何无法直接quit()。

1 对象创建

1.1 Looper 创建

应用启动时会在ActivityThread的mian方法里进行lLooper的创建具体方法为

 

 这里具体初始化的方法为prepare(false)

下面的同步方法是为了确保一个线程中只有单个Looper,重复初始化时会出现异常

 

 ThreadLocal可以理解为一个Map集合 这里是以线程为key,Looper为Value 以键值对的形式存储线程对应的Looper

创建Looper对象之前 会先从ThreadLocal中获取当前线程key的对象,如果获取到了则当前线程中已经有对应的对象,不能重复创建抛出异常如果没有则调用Looper的私有构造方法将其放入ThreadLocal.

 这是Looper的构造方法因为是私有所以只能通过prepare来进行创建,在prepare做了线程为key的检索确保了一个线程中只有一个Looper为我们在多线程中创建线程唯一对象提供了思路

在构造方法中将MessageQueue的对象创建出来,每个Looper都有对应的MessageQueue对象

至此Looper创建完成

1.2 MessageQueue的创建

由上面代码可以看出MessageQueue是在Looper的构造方法中创建,由于Looper的线程唯一的创建方式所以MessageQueue在单个线程中也只有一个唯一的对象

1.3 Handle的创建

 如图所示,Handle有3种创建方式

handle的创建方式用的是以下构造方法

 hanldle1的创建方式用的是以下构造方法

 handle2的创建构造方法与handle1相同

 最终都会进入同一个底层的构造方法 在此方法中可以看到handler中通过mLooper = Looper.myLooper()拿到了当前线程的Looper对象

接下来会对Looper对象进行非空判断拿到Looper对象后拿到了Looper的MessQueue对象

以及对回调方法callback进行赋值

1.4 Message

Message对象是在进行Handler通信中直接new出来的一个对象通过

Message msg = Message.obtain()拿到

 可以看到obtain中会尝试拿空闲的Message对象进行复用如果没有会直接返回一个新的对象

2 信息的发送

信息的发送方式有以下几种 可以传入Message对象也可以只传入what只传入what时也会创建Message对象

最终都会进入enqueueMessage方法 这里为msg.target进行了赋值

 从上图可知Message在添加进MessageQueue队列之前也将当前的Handler对象存入;

通过sendMessageAtTime拿到了Handler初始化时从Looper中获得的MessageQueue对象

最终调用MessageQueue的enqueueMessage方法将Message对象添加进MessageQueue中

 3 信息的取出与处理

在ActivityThread的main方法中在Looper.prepareMainLooper()之后还执行了一个Looper的方法

 可以看到 Loop中法中先拿到了本线程的Looper通过本线程的Looper对象拿到了MessageQueue对象

内部维护了一个for死循环通过queue.next()取出消息

在时间比对操作之后 最终会执行msg.target.dispatchMessage(msg)

从之前的代码中可以看出msg.target是消息在放入MessageQueue之前将生成消息的Handle对象赋值进去,所以此方法是回调Handle的dispatchMessage方法 可以看出此方法有3种回调逻辑,对应Handle的3种创建方式 handle对应2

handle1对应3

handle2对应1

hanlde的创建方式在传入时会将Handler.Callback赋值给mCallBack,当消息执行完毕时会进行2进行回调返回true或者false没有任何影响,因为返回false的话会执行图标记为3的handleMessage方法,此方法在源码中为空实现

hanlde1在构建时没有传入参数,所以只能走到3

/**

* Subclasses must implement this to receive messages.

*/

public void handleMessage(@NonNull Message msg) {

}

这个handleMessage在源码中是空的实现,但是在handle1构建的时候重写了handleMessage方法,所以这个方法也能正常执行

handle2在日常使用时通常是使用post方法 post方法中还是会调用sendMessage方法 通过getPostMessage方法获取一个Message对象并将Runnable赋值给message的callback属性满足diapatchMessage方法中的条件1

private static void handleCallback(Message message) {

message.callback.run();

}

将run方法在handle的创建线程中执行掉

至此 Hanlder执行完成

4 总结

应用启动时在AcitivyThread的main方法中通过Loop.prepare进行了Looper与MessageQueue对象的创建,通过Looper.loop方法启动内部的for死循环从MessageQueue中通过.next()方法获取Message对象,最终通过Mesage中的handle对象执行dispatchMessage的回调方法,实现跨线程的功能

Handle在创建时会拿到当前线程的Looper对象,通过Loop对象拿到MessageQueue的对象通过enqueueMessage方法将Mesage对象放入MessageQueue中

5 子线程Handler实现将代码添加几行日志 

发现Loop方法一直没有结束可以确定loop方法内部维护的确实是死循环

结束子线程Looper释放Thread资源

通过代码发loop的死循环中有这样的判断条件

当消息队列执行了quit方法时循环关闭 Looper中有quit()的方法

执行MessageQueue的quit方

测试一下 

 

 执行成功run方法执行完毕线程销毁

那主线程的Looper能不能也这样停止呢

测试代码

查看源码发现这个异常是在MessageQueue中的quit()方法中抛出,主线程不能被quit

 主线程的loop为什么不会被死循环阻塞

native方法实现

实现流程图

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值