Handler机制是面试中的常客了,今天和大家一起通过源码层面来解析一下。

前提知识点

Handler机制涉及到几个类:MessageQueue, Looper, Message, ActivityThread。

- ActivityThread: 主线程,开启loop循环,管理application进程, 调度管理activity, 广播及其他操作。

- Message: handler机制中消息的载体,包含相关描述和数据对象,包含属性what, obj, arg1,arg2等。

- MessageQueue: 消息队列。

- Looper:循环去MessageQueue中消息。

AppCompatActivity.java-> FragmentActivity.java->SupportActivity.java->Activity.java

在Activity中默认有一个ActivityThread这是一个main thread,其中final Looper mLooper = Looper.myLooper();实例化了一个looper对象。

在ActivityThread.java中的main方法中,有如下代码

```

public static void main(String[] args) {

...省略其他代码

Looper.prepareMainLooper();

...省略其他代码

Looper.loop();

}

```

```

Looper.java

public static void prepareMainLooper() {

prepare(false);

synchronized (Looper.class) {

if (sMainLooper != null) {

throw new IllegalStateException("The main Looper has already been prepared.");

}

sMainLooper = myLooper();

}

}

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));

}

// Looper构造函数中初始了消息队列MessageQueue对象

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}

```

创建looper 对象之前,会判断 sThreaLocal 中是否已经绑定过 Looper 对象,如果是则抛出异常,确保一个线程中Looper.prepare()只调用一次。

如果在MainActivity中调用,如下代码,会报错。

通过上述代码可以得知,Activity中默认开始了loop()循环,用于获取handler发送的消息。

最简单的应用

```

public class MainActivity extends AppCompatActivity implements View.OnClickListener {private final String TAG = "MainActivity";public final int MSG_DOWN_FAIL = 1;public final int MSG_DOWN_SUCCESS = 2;public final int MSG_DOWN_START = 3;@BindView(R.id.btn_start_thread)Button btnStart;@BindView(R.id.tv_status)TextView tvShow;private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_DOWN_START:tvShow.setText("down start");break;case MSG_DOWN_SUCCESS:tvShow.setText("down success");break;case MSG_DOWN_FAIL:tvShow.setText("down fail");break;default:break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);btnStart.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_start_thread:new MyThread().start();break;default:break;}}class MyThread extends Thread {@Overridepublic void run() {handler.sendEmptyMessage(MSG_DOWN_START);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}Message msg = Message.obtain();msg.what = MSG_DOWN_SUCCESS;handler.sendMessage(msg);}}}

```

sendMessage之后发生了什么

```

public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0);}

```

sendMessage调用了sendMessageDelayed->sendMessageAtTime->enqueueMessage, 最终调用了MessageQueue的enqueueMessage的方法, 并将自己设置为message的target

```

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}

```

接下来就到了消息队列MessageQueue中了,来看一下

- 如果message的target为null,则直接抛出异常

- 按照message的时间when有序插入到MessageQueue中,所以MessageQueue是一个按时间排序的有序队列。

怎么取MessageQueue中的消息

其实这里就是Looper.loop()方法从消息队列中不断循环取消息了。

不断调用MessageQueue的next()方法取消息,如果message不为null, 则调用handler的dispatchMessage分发消息。

至此 Handler 的发送消息和消息处理流程已经介绍完毕。

面试常见问题

1.Looper.loop() 为什么不会阻塞主线程

Android的Ui线程,开启了一个死循环,但是并没有阻塞主线程是为什么呢?

在MessageQueue的next方法中有这样一行代码

```

private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/

```

nativePollOnce 方法是一个 native 方法,当调用此 native 方法时,主线程会释放 CPU 资源进入休眠状态,直到下条消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制。

2.Handler 的 sendMessageDelayed 或者 postDelayed 是如何实现的

messageQueue的enqueueMessage是按照消息的when时间有序加入到队列的,取的时候也是按照时间进行取的

可以看到如果当前时间小于msg设置的时间,会计算一个timeout,在timeout到了之后,才会将UI线程唤醒,交由CPU执行。

总结

1.APP启动创建主线程的时候会通过ActivityThread执行Looper.prepare(),创建一个looper 对象,在私有的构造方法中又创建了 MessageQueue 作为此 Looper 对象的成员变量,Looper 对象通过 ThreadLocal 绑定 MainThread 中。

2.在创建Handler对象的时候,通过构造函数获取ThreadLocal 绑定的looper对象,并通过looper获取消息队列MessageQueue作为成员变量

3.子线程发送消息时,将msg的target设置为handler自身,之后调用成员MessageQueue的enqueueMessage将消息按照msg.when时间排序插入到消息队列

4.主线程通过Looper.loop()开启不阻塞UI线程的死循环,通过绑定的looper对象获取MessageQueue,调用next()方法不断获取msg, 并通过(handler)msg.target.dispatchMessage发送至我们创建的handler时覆盖的handleMessage()方法

一图以蔽之

--- END ---

java的handler机制_从源码解析Handler机制相关推荐

  1. Spring5源码 - 13 Spring事件监听机制_@EventListener源码解析

    文章目录 Pre 概览 开天辟地的时候初始化的处理器 @EventListener EventListenerMethodProcessor afterSingletonsInstantiated 小 ...

  2. Handler 源码解析——Handler的创建

    前言 Android 提供了Handler和Looper来来满足线程间的通信,而前面我们所说的IPC指的是进程间的通信.这是两个完全不同的概念. Handler先进先出原则,Looper类用来管理特定 ...

  3. Java集合框架之三:HashMap源码解析

    Java集合框架之三:HashMap源码解析 版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! HashMap在我们的工作中应用的非常广泛,在工作面试中也经常会被问到,对于这样一个重要的集 ...

  4. 源码解析——消息机制

    映象笔记的链接:源码解析--消息机制 本文转自wauoen51CTO博客,原文链接: http://blog.51cto.com/7183397/1968269,如需转载请自行联系原作者

  5. Dubbo源码解析-——SPI机制

    文章目录 一.什么是SPI机制 二.Java原生的SPI机制 2.1.javaSPI示例 2.1.1.编写接口和实现类 2.1.2.编写配置文件 2.1.3.通过SPI机制加载实现类 2.1.4.JA ...

  6. 源码解析 Handler 面试宝典

    Handler 面试源码解析面试宝典 前言 1.一个线程有几个Handler 考点 答案 2.一个线程有几个Looper?如何保证 考点 答案 3.Handler 内存泄漏原因?为什么其他的内部类没有 ...

  7. 消息转发机制与Aspects源码解析

    前言 最近在搞重构相关的事情,遇到了不少这样的场景: 进入一个界面,在viewWillAppear:的时候做相应判断,如果满足条件则执行对应代码. 这类业务有一个特点,业务内容是对应整个App的,与对 ...

  8. Set接口的源码解析+扩容机制

    Set集合 Set的接口介绍 无序(添加和需要的顺序是不一样的)没有索引 不允许重复元素,所以最多包含一个null 添加和取出的顺序是不固定的. 虽然不是添加的顺序,但是顺序是固定的 Set的常用方法 ...

  9. 【Java】HashMap的数据结构、源码解析 - 公开课笔记

    主要内容 Hashmap的数据结构 HashMap实现原理 HashMap源码解析 HashMap底层的数据结构? 1.7之前:数组+链表 1.8之后:数组+链表+红黑树 bucket 1.7之前: ...

  10. 【Android 异步操作】Handler 机制 ( Android 提供的 Handler 源码解析 | Handler 构造与消息分发 | MessageQueue 消息队列相关方法 )

    文章目录 一.Handler 构造函数 二.Handler 消息分发 三.MessageQueue 消息队列相关函数 一.Handler 构造函数 一般使用 Handler 时 , 调用 Handle ...

最新文章

  1. iPhone系列设备媒体查询:
  2. ios view 切上部分圆角_ios – 具有圆角的UIView:如何正确剪辑子视图?
  3. ubuntu开启root登陆
  4. ADO.NET 4.5中的异步与流特性
  5. html做转盘指针被压住,爸爸特制“写作业”转盘,被儿子反套路:愿赌服输!...
  6. BZOJ 4259: 残缺的字符串 [FFT]
  7. android简单小程序课程设计,微信小程序课程设计报告
  8. 为树莓派制作系统镜像时进行瘦身,方便后续保存与批量写入
  9. 等值线/面生成一站式封装
  10. 在win10下,xilinx公司FPGA下载器上,指示灯不亮,设备管理器中驱动显示正常,下载器无法识别到期间
  11. excel上下标录入技巧
  12. hadoop2.6伪分布+pig0.15+zookeeper3.4.6安装
  13. django3.x+DRF+simpleui+uniapp打造自己的任务推广(兼职、悬赏)平台
  14. robotframework报错
  15. origin如何绘制双y轴曲线_origin怎么画双y轴 看完恍然大悟
  16. 【Web_接口爬虫_Python3_58同城_requestosetreeproxies】58同城,商铺出租,爬取标题、内容、链接地址,保存文本_20200401
  17. nvm use 报错:You do not have sufficient privilege to perform this operation
  18. java捕获唯一约束异常_java – 捕获JPA上唯一约束的原因
  19. 畅聊微信支付遇到的坑
  20. 2021.5.10(cf)

热门文章

  1. Volley 源码解析(一)
  2. socks代理转http代理
  3. nero刻录软件免费版_如何通过免费替代品获得Nero的最佳功能
  4. 专访Wunderlist主设计师Jan Martin:永远不要盲目跟风流行趋势1
  5. GAN与自动编码器:深度生成模型的比较
  6. win10 如何做到 C盘 的绝对干净,所有软件都安装到D盘,C盘只用来存操作系统。
  7. android 设置-关于手机-连续点击版本 打开开发者模式的功能实现
  8. 苹果NFC功能以及Apple Pay的初探
  9. vue web在线聊天功能实现
  10. 关于ie6常见浏览器兼容问题