1 Handler存在的意义
- 跟web开发的ajax有异曲同工之妙
- 使得Android开发难度大大降低
- 几乎看不到多线程死锁问题 Handler是android线程通信框架,完全解决掉Android中线程通信、线程切换的问题。Handler设计符合迪米特法则(最少知道原则),使用简单。
线程通信实现技术方案:内存共享。
2 Hadler四要素
与Hadler相关的类:
-
Message(消息): 通过Message.obtain()来从消息池中获得空消息对象,以节省资源,避免内存抖动。
-
MessageQueue(消息队列):保存Message,属于Looper对象。
-
Looper(消息循环):
一个Thread对应一个Looper对象。创建Handler之前需调用Looper.prepare(),执行Looper的构造方法,初始化消息队列,并且指定当前线程。负责消息的入列和出列。
-
Handler(消息发送和处理):
3 Handler源码分析
Handler工作流程可以理解成一个传送带工作的流程:
Looper.loop开启传送,Thread提供动力。
3.1 Handler
-
消息入队列
handler.send——>Handler.enequeueMessage——>MessageQueue.enquueuMessage
-
消息出队列 Looper.loop()——>MessageQueue.next——>handler.dispatchMessage()——>handler.handleMessage
3.2 MessageQueue
Message通过new Message或obtain获取,其实是获取到一块内存,message在不同线程间传递,就实现了内存共享,线程切换。
数据结构:MessageQueue是由单链表实现的优先级队列。
-
单向链表
Message都有一个next节点。
Message->new(Message)->new(Message)...
-
优先级
Message有时间属性
MessageQueue.enquueuMessage入队列排序算法:插入排序
-
队列
先进先出
3.3 Looper源码
一个线程只有一个looper
-
Looper.prepare:
调用构造函数,生成MessageQueue,获取当前线程,实现looper与thread的绑定,并把 Looper对象set到ThreadLocal。
-
Looper.loop
Looper.loop是一个死循环,从messageQueue中获取Message,并使用handler进行分发。
loop有两种可能会阻塞:
1) message 不到时间 ,自动唤醒
2) messageQueue为空,无限等待
退出应用 Looper.quite可结束looper.loop()循环
4 Handler面试题
-
一个线程有几个Handler?
多个,可以在线程中new多个Handler。
-
一个线程有几个Looper?如何保证?
一个线程只有个Looper。
Looper的构造函数是私有的。Looper.prepare中,首先获取ThreadLocal中存储的looper,如果不为空,抛异常,为空new looper,set到ThreadLocal中。其中get和set使用的是同一个ThreadLocal。
-
Handler 内存泄漏原因? 为什么其他的内部类没有说过有这个问题?
内部类默持有外部类的对象 message handler.enqueueMessage中,把handler赋值给message.target,故message持有handler,handler持有activity。关闭activity时,如果message还没有处理,message占用的内存没有释放,意味着activity对象还被持有,故activity内存不能释放。
-
为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备? 子线程中new Handler需要调用Looper.perpare和Looper.loop。主线程已在ActivityThread的main函数中调用了这连个方法。
-
子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
调用handler.getLooper().quite(); 消息队列无消息时,looper.loop()的循环会一直阻塞在MessageQueue.next的nativePollOnce处,线程资源无法释放,调用quite后,会通过native唤醒线程,MessageQueue.next返回null,looper.loop循环退出,线程结束,释放线程资源。
主线程调用quite会抛异常。
-
既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?
MessageQueue存、取消息都加了synchronized锁,synchronized锁住的对象是MessageQueue实例,确保一个线程同一时刻只有一个可以操作队列的地方。
synchronized:JVM内置锁,可以用于代码块、方法
-
我们使用 Message 时应该如何创建它?
调用Message.obtain,从消息池中获得空消息对象,避免因多次new Message,分配、释放内存造成内存抖动,带来的内存碎片问题(享元模式:内存复用)。
-
Looper死循环为什么不会导致应用卡死? ANR:5s内无法响应用户输入事件或BroadcastReceiver在10s内无法结束。
用户输入事件最终会被封装成Message,用handler处理,如果5s内没有被处理,会通过handler发送ANR提醒,弹框。
子线程执行完代码后,声明周期结束,线程退出。主线程肯定不能运行一段时间后自动结束,主线程的loop循环保证了主线程代码可以一直执行。
主线程息队列无消息时,looper.loop()的循环会一直阻塞在MessageQueue.next的nativePollOnce处,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。
这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
5 ThreadHandler
HandlerThread继承与Thread,run方法中创建Looper对象,并调用了Looper.prepare、looper.loop(),本质上是Thread+Looper。方便Handler在子线程中使用,初始化Looper和获取Looper时,都加了同步锁,保证了线程安全。
HandlerThread handlerThread = new HandlerThread("");
handlerThread.run();
new Handler(handlerThread.getLooper());
handlerThread.quit();
6 IntentService
Service不是运行在独立的线程,所以不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。Service中的耗时操作,需要单独创建线程执行。
IntentService
IntentService是系统磅我们创建了一个线程,并提供了一个回调方法onHandlerIntent,在此方法中可进行耗时操作。
IntentService在onCreate方法中,创建了HandlerThread,获取HandlerThread的looper,创建Handler,handler是在子线程中执行的。
onStart方法中,通过Handler发送消息,handleMessage会调用onHandlerIntent,重写onHandlerIntent处理业务。
-
可用于执行后台耗时的任务,任务执行后会自动停止。在IntentService内部的Handler.handleMessage中,调用onHandlerIntent会调用stopSelf
-
具有高优先级,适合高优先级的后台任务,且不容易被系统杀死。内部创建子线程时,优先级为0。
-
可以多次启动,每个耗时操作都会以工作队列的方式在IntentService的onHandleIntent回调方法中执行。