
Handler是一个消息传递机制,作用是将子线程需要的UI操作,传递到UI线程执行,保证线程安全。

相关类介绍
- Message:线程间通讯的数据单元。
MessageQueue:消息队列,特点先进先出。存储Handler发送过来的消息(Message)
-
Handler:是一个消息处理器,将消息传递给消息队列,然后再由对应线程从消息队列中逐个取出数据,并执行
-
Looper:消息循环器,循环取出消息(Message),将取出的消息分发给对应的处理者(Handler)。 特点:
-
一个Handler只能拥有一个Looper。
-
一个Looper可以绑定多个Handler
public Handler(Callback callback, boolean async) {
...
//关联Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//关联MessageQueue
mQueue = mLooper.mQueue;
...
}
在Handler的构造方法中,会关联Looper和MessageQueue(Looper的构造函数中会创建自己的MessageQueue)
源码分析
Handler消息传输机制分析
默认情况,消息队列只有一个,就是主线程的消息队列。这个线程由ActivityThread.main方法中创建,ActivityThread的main方法是整个APP的入口,调用Looper.prepareMainLooper()创建,执行Looper.loop()来启动消息循环。
public static void main(String[] args) {
Looper.prepareMainLooper();
// 1.创建主线程Looper
ActivityThread thread = new ActivityThread();
// 2. 创建主线程
Looper.loop();
// 3. 开启消息循环
}
//设置和获取Looper,Looper保存在ThreadLocal中
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//设置Looper,只能调用一次
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
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到Looper存储在ThreadLocal,这就实现了线程安全。Looper可以对应多个Handler
/**
* 源码分析: Looper.loop()
* 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
* 特别注意:
* a. 主线程的消息循环不允许退出,即无限循环
* b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
*/
public static void loop() {
...// 仅贴出关键代码
// 1. 获取当前Looper的消息队列
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
// 即loop()执行前必须执行prepare(),从而创建1个Looper实例
final MessageQueue queue = me.mQueue;
// 获取Looper实例中的消息队列对象(MessageQueue)
// 2. 消息循环(通过for循环)
for (;;) {
// 2.1 从消息队列中取出消息
Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息队列里的消息
// 若取出的消息为空,则线程阻塞
// 2.2 派发消息到对应的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派发给消息对象msg的target属性
// target属性实际是1个handler对象
// 3. 释放消息占据的资源
msg.recycle();
}
}
loop()是一个消息无限循环,不断从消息队列获取消息,将消息派发到对应的Handler
public final class Message implements Parcelable {
Handler target;
Runnable callback;
Message next;
}
/** dispatchMessage(msg)
* 定义:属于处理者类(Handler)中的方法
* 作用:派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public void handleMessage(Message msg) {
... // 创建Handler实例时复写
}
dispatchMessage派发消息最终会调用HandlerMessage或者run方法处理
- 当调用Handler.post时msg.callback != null,会走run方法
- 当Message.obtain方法里设置时msg.callback != null,会走run方法
- 当Handler初始化时传入Callbackh时mCallback != null,会走mCallback.handleMessage(msg)方法
- 当前面两者都没有,直接调用sendMessage时,会走handleMessage(msg) 方法
MessagePool

从消息池里拿一个消息,并表尾删除这个消息,消息池大小减一

消息池最大容量是50

recycleUnchecked()在loop的for每个循环的最后就调用,重置message,如果消息池没满,就添加到消息池里(表尾插入),否则就丢弃这个message。
消息池是一个链表,最大容量是50
使用线程池的原因是复用message,避免message重复销毁创建
MessageQueue
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
消息被处理的时间 = 当前时间+延迟的时间
消息被处理的时间 = 当前时间+延迟的时间?
至于这个地方为什么要用SystemClock.uptimeMillis() 而不用SystemClock. currentTimeMillis(),
System.currentTimeMillis() 这个方法得到的毫秒数为“1970年1月1号0时0分0秒 到 当前手机系统的时间”的差
SystemClock.uptimeMillis()用来计算自开机启动到目前的毫秒数。如果系统进入了深度睡眠状态(CPU停止运行、显示器息屏、等待外部输入设备)该时钟会停止计时。因此SystemClock.uptimeMillis()方法也成为了计算间隔的基本依据,比如Thread.sleep()、Object.wait()、System.nanoTime()以及Handler都是用SystemClock.uptimeMillis()方法。这个时钟是保证单调性,适用于计算不跨越设备的时间间隔。
MessageQueue中最重要的就是两个方法: 1.enqueueMessage向队列中插入消息 2.next 从队列中取出消息
Handler.sendMessageDelayed()方法最终会调用enqueueMessage方法进入MessageQueue的enqueueMessage方法中:
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
if (mQuitting) {//表示此消息队列已经被放弃了
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;//将延迟时间封装到msg内部
Message p = mMessages;//消息队列的第一个元素
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//如果此队列中头部元素是null(空的队列,一般是第一次),或者此消息不是延时的消息,
//则此消息需要被立即处理,此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,
//然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//如果此消息是延时的消息,则将其添加到队列中,
//原理就是链表的添加新元素,按照when,
//也就是延迟的时间来插入的,延迟的时间越长,越靠后,
//这样就得到一条有序的延时消息链表,取出消息的时候,
//延迟时间越小的,就被先获取了。
//插入延时消息不需要唤醒Looper线程。
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {//唤醒线程
nativeWake(mPtr);
}
}
return true;
}
MessageQueue中enqueueMessage方法的目的有两个: 1.插入消息到消息队列 2.唤醒Looper中等待的线程(如果是及时消息并且线程是阻塞状态)
MessageQueue的底层数据接口是单向链表,mMessages成员变量
取出消息的方法next():
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
//从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,
//mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,
//阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。
//最终会使loop的for中break出循环
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
// 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,
//进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞
//;
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//正常取出消息
//设置mBlocked = false代表目前没有阻塞
mBlocked = false;
if (prevMsg != null) {//prevMsg!=0的情况先不考虑
prevMsg.next = msg.next;
} else {////链表头部移动到链表第二个元素上
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
//没有消息,会一直阻塞,直到被唤醒
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
}
}
demo
public class HandlerActivity extends AppCompatActivity {
@BindView(R.id.content_tv)
TextView mContentTv;
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
mContentTv.setText((CharSequence) msg.obj);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
ButterKnife.bind(this);
}
@OnClick({R.id.post, R.id.obtain, R.id.Handler_Callback_is_null, R.id.Handler_Callback_is_not_null})
public void onClick(View view) {
switch (view.getId()) {
//当调用Handler.post时msg.callback != null,会走run方法
case R.id.post:
new Thread() {
@Override
public void run() {
super.run();
mHandler.post(new Runnable() {
@Override
public void run() {
mContentTv.setText("post");
}
});
}
}.start();
break;
//当Message.obtain方法里设置时msg.callback != null,会走run方法
case R.id.obtain:
new Thread() {
@Override
public void run() {
super.run();
Message message = Message.obtain(mHandler, new Runnable() {
@Override
public void run() {
mContentTv.setText("obtain");
}
});
mHandler.sendMessage(message);
}
}.start();
break;
//当Handler初始化时传入Callback时mCallback != null,会走mCallback.handleMessage(msg)方法
case R.id.Handler_Callback_is_null:
new Thread() {
@Override
public void run() {
super.run();
Message msg = Message.obtain();
msg.what = 1; // 消息标识
msg.obj = "Handler_Callback_is_null"; // 消息内存存放
mHandler.sendMessage(msg);
}
}.start();
break;
//当前面两者都没有,直接调用sendMessage时,会走handleMessage(msg) 方法
case R.id.Handler_Callback_is_not_null:
final Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 2) {
mContentTv.setText((CharSequence) msg.obj);
}
return true;
}
});
new Thread() {
@Override
public void run() {
super.run();
Message msg = Message.obtain();
msg.what = 2; // 消息标识
msg.obj = "Handler_Callback_is_not_null"; // 消息内存存放
handler.sendMessage(msg);
}
}.start();
break;
}
}
}
为什么可以直接post呢,因为post(Runnable r)会将Runnable包装成Message对象,并将Runnable对象设置给Message对象的callback,最好将Message对象插入消息队列中去。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(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);
}
public boolean sendMessageAtTime(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;
}
//将消息插入MessageQueue
return enqueueMessage(queue, msg, uptimeMillis);
}
Handler机制流程图

问题
1、为什么不new Message()而采用Message.obtain()?
因为Message内部维护了1个Message池,用于Message消息对象的复用,使用obtain()则是直接从池内获取,避免内存new Message重新分配内存。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
2、Handler可以在子线程创建吗?
通常Handler必须在主线程创建,因为Handler机制的主要作用是更新UI。当然也可以创建在子线程,那么就得自己手动去prepare和loop。
new Thread(){
@Override
public void run() {
super.run();
//创建当前线程的Looper,并绑定到ThreadLocal中
Looper.prepare();
//创建Handler,关联Looper和MessageQueue
Handler handler = new Handler();
//启动消息循环
Looper.loop();
}
}.start();
3、Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。
这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质是同步I/O,即读写是阻塞的。 主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。 blog.csdn.net/chewbee/art…
管道
管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。 当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。 当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。
epoll机制
IO多路复用
单个线程,通过记录跟踪每个I/O流(sock)的状态,来同时管理多个I/O流 。 多路网络连接复用一个io线程。]
Socket 它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据
4、如何避免不当导致的内存泄漏
解决方案
- 1.尽可能避免使用Handler做延迟操作。
- 2.使用静态内部类继承Hanlder(静态内部类不会持有外部对象的引用),如果我们需要在Handler中 使用外部的Activity时,可以定义一个Activity弱引用(WeakReference)对象,弱引用在第二次GC回收时,可以被回收。
- 3.在onDestory时,清除Handler消息队列中的消息removeCallbacksAndMessages(null)
问题根源: 非静态匿名内部类持有外部类引用
静态Handler内部类, 如果在handMessage方法里需要用到上下文, 进行弱化,在onDestory时,清除Handler消息队列中的消息removeCallbacksAndMessages(null)
public class NoLeakActivity extends AppCompatActivity {
private NoLeakHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new NoLeakHandler(this);
Message message = Message.obtain();
mHandler.sendMessageDelayed(message,10*60*1000);
}
private static class NoLeakHandler extends Handler{
private WeakReference<NoLeakActivity> mActivity;
public NoLeakHandler(NoLeakActivity activity){
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
}
但是为什么为static类型就会解决这个问题呢? 因为在java中所有非静态的对象都会持有当前类的强引用,而静态对象则只会持有当前类的弱引用。 声明为静态后,handler将会持有一个Activity的弱引用,而弱引用会很容易被gc回收,这样就能解决Activity结束后,gc却无法回收的情况。
5、什么是handler?
一套 Android 消息传递机制 / 异步通信机制
Handler可以发送和处理消息对象或Runnable对象,这些消息对象和Runnable对象与一个线程相关联。每个Handler的实例都关联了一个线程和线程的消息队列。当创建了一个Handler对象时,一个线程或消息队列同时也被创建,该Handler对象将发送和处理这些消息或Runnable对象。
handler类有两种主要用途:
- 执行Runnable对象,还可以设置延迟。 - 两个线程之间发送消息,主要用来给主线程发送消息更新UI。
2、为什么使用handler?
解决多线程并发问题,假设如果在一个activity中,有多个线程去更新ui,并且都没有加锁机制,那界面显示肯定会不正常。于是andoird官方就封装了一套更新ui的机制,也可以用handler来实现多个线程之间的消息发送。
在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理
3、怎么使用handler?
handler常用的方法有以下这些:
- post(Runnable)发送即时消息
- postAtTime(Runnable,long)
- postDelayed(Runnable,long)发送延时消息
- sendEmptyMessage(int)发送空消息
- sendMessage(Message)发送即时消息
- sendMessageAtTime(Message,long)
- sendMessageDelayed(Message,long)发送延时消息
4、为什么Android要设计只能通过Handler机制更新UI?
最根本的目的就是解决多线程并发问题
假设如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样的问题?(更新界面错乱)如果对更新UI的操作都进行加锁处理,又会产生什么样的问题?(性能下降)
基于对以上目的的考虑,android给我们提供了一套更新UI的机制,我们只需遵循这样的机制,无需考虑多线程问题。
5、Looper、MessageQueue、Message、Handler的关系
6、Handler异步消息处理(HandlerThread)
7、子线程可以和主线程通信吗?主线程可以和子线程通信吗?
8、Android在子线程用handler发送的消息,主线程是怎么loop到的?
主线程会创建loop
10、sleep和wait区别?
sleep是让线程休眠,wait是等待。
使用上:从使用角度看,sleep是Thread线程类的方法,而wait是Object顶级类的方法。
sleep可以在任何地方使用,而wait只能在同步方法或者同步块中使用。
CPU及资源锁释放:sleep,wait调用后都会暂停当前线程并让出cpu的执行时间,但不同的是sleep不会释放当前持有的对象的锁资源,到时间后会继续执行,而wait会放弃所有锁并需要notify/notifyAll后重新获取到对象锁资源后才能继续执行。
异常捕获:sleep需要捕获或者抛出异常,而wait/notify/notifyAll不需要。
11、谈一下你对ThreadLocal的了解?
Android源码学习(4) Handler之ThreadLocal
14、非UI线程真的不能更新UI吗?
总结:当检查当前线程是否是主线程这个方法checkThread()执行后,才不能在子线程更新UI。在此之前是可以在子线程中更新UI的
15、在子线程中创建Handler报错是为什么?
没有lopper.prepare
16、如何在子线程创建Looper?
Looper.prepare();
17、为什么通过Handler能实现线程的切换?
Handler.post的逻辑在哪个线程执行,是由Looper所在线程还是Handler所在线程决定的? 是由Looper所在线程决定的
最终逻辑是在Looper.loop()方法中,从MsgQueue中拿出msg,并且执行其逻辑,这是在Looper中执行的,因此由Looper所在线程决定。
18、Handler的post/send()的原理?
建议看一下源码: 通过一系列sendMessageXXX()方法将msg通过消息队列的enqueueMessage()加入到队列中。
19、Handler的post方法发送的是同步消息吗?可以发送异步消息吗?
用户层面发送的都是同步消息 不能发送异步消息 异步消息只能由系统发送。
20、Handler的post()和postDelayed()方法的异同?
底层都是调用的sendMessageDelayed() post()传入的时间参数为0 postDelayed()传入的时间参数是需要的时间间隔。
21、Handler构造方法中通过Looper.myLooper();是如何获取到当前线程的Looper的?
myLooper()内部使用ThreadLocal实现,因此能够获取各个线程自己的Looper
22、Looper.loop()是如何阻塞的?MessageQueue.next()是如何阻塞的?
通过native方法:nativePollOnce()进行精准时间的阻塞。
23、Looper的两个退出方法以及它们的区别?
quit: quitSafely:
24、Looper.loop()的源码流程?
1.获取到Looper和消息队列
2.for无限循环,阻塞于消息队列的next方法
3.取出消息后调用msg.target.dispatchMessage(msg)进行消息分发
25、Looper.loop()在什么情况下会退出? 1.next方法返回的msg == null
2.线程意外终止
26、Looper.quit/quitSafely的本质是什么? 1.让消息队列的next()返回null,依次来退出Looper.loop()
ThreadLocal的原理 thread.threadLocals就是当前线程thread中的ThreadLocalMap ThreadLocalMap中有一个table数组,元素是Entry。根据ThreadLocal(需要转换获取到Hash Key)能get到对应的Enrty。 Entry中key为ThreadLocal, value就是存储的数值
27、如何获取到当前线程?
Thread.currentThread()就是当前线程
28、如何在ThreadLocalMap中,ThreadLocal如何作为键值对中的key?
通过ThreadLocal计算出Hash key,通过这个哈希值来进行存储和读取的。
29、Handler是怎么做到消息延时发送的
学习参考:
Android开发进阶从小工到专家