前言
看到这个标题,大家肯定有些疑惑,网上关于Hanlder源码分析的文章已经够多了,你为什么还要写?我不否认这类文章真的是一抓一大把,但纸上得来终觉浅,绝知此事要躬行。如果真的想把知识点搞清楚,自己必须要通过查看其源码、明白其工作流程、领悟其设计思想,最终转化成自己的东西。
Hnadler介绍
在实际的开发过程中,我们经常将耗时的操作(比如网络请求)放到一个子线程中去,这是因为Android主线程中不允许做耗时操作,又不允许在子线程中操作UI,如何将子线程中得到的数据交给主线程去更新UI呢?这个时候就轮到Hanlder登场了,类似的操作方式还有很多,有兴趣的小伙伴可自行探究。其实Handler不仅仅局限于主线程中创建,子线程中创建也是可以的,只是我们平时用的最多的还是第一种形式。
内部重要类
在讲具体讲Hanlder机制之前,还是要了解一下其中几个重要的类:Handler、Message、MessageQueue、Looper。
Hanlder: 具体负责消息的发送和处理
Message: 将要传递的数据封装成一个Message对象,进行传输
MessageQueue: 消息队列,是一种数据结构,用于存储Hanlder发送过来的消息(Message对象)
Looper: 消息轮询器,将MessageQueue中存储的消息(Message对象)发送给对应的Handler
由这几个类的作用我们可以简要的概括一下Handler机制:
Handler发送一个消息(Message对象)到消息队列(MessageQueue)中去,通过消息轮询器(Looper对象)
不断的从消息队列中取出消息,发送给对应Handler去处理的过程。
在发消息时,Handler是消息的发送者;在处理消息时,Handler是消息的接受者。
使用方法
到这里我们已经知道Handler机制中几个重要类的作用,接下来就让我们看一下具体的用法。
子线程到主线程
public class MainActivity extends AppCompatActivity {
private TextView tv;
//1、定义Handler子类
class MyHanlder extends Handler {
@Override
public void handleMessage(Message msg) { //2、重写handleMessage方法
//具体要实现的UI操作
switch (msg.what) {
case 0:
tv.setText(msg.obj.toString());
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.tv);
//3、主线程创建Hadler对象
final MyHanlder myHanlder = new MyHanlder();
//创建子线程1
new Thread(new Runnable() {
@Override
public void run() {
try {//延时两秒
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
//4、创建消息对象,并赋值
Message message = Message.obtain();
message.what = 0;
message.obj = "线程1发来贺电";
myHanlder.sendMessage(message); //5、sendMessage方式发送消息
}
}).start();
//创建子线程2
new Thread(new Runnable() {
@Override
public void run() {
try {//延时四秒
Thread.sleep(4000);
} catch (Exception e) {
e.printStackTrace();
}
//post方式发送消息
myHanlder.post(new Runnable() {
@Override
public void run() {
tv.setText("线程2发来贺电");
}
});
}
}).start();
}
}
来看一下运行效果

子线程到子线程
public class MainActivity extends AppCompatActivity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//子线程中创建Hadler
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();"-------------注意点1----------------"
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
Log.i("MainActivity", msg.obj.toString());
break;
case 1:
Log.i("MainActivity", msg.obj.toString());
break;
}
}
};
Looper.loop();"-------------注意点2----------------"
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {//延时两秒
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.what = 0;
message.obj = "线程一再次发来贺电";
handler.sendMessage(message);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {//延时四秒
Thread.sleep(4000);
} catch (Exception e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.what = 0;
message.obj = "线程二再次发来贺电";
handler.sendMessage(message);
}
}).start();
}
}
看一下运行结果:

但是在编写这段代码时出现了连个要注意的地方,如上面代码所示。 1、假设注意点1处和注意点2处的代码都没有,再运行一次会出现如下图所示的错误

2、假设只有注意点1的代码,没有注意点2的代码会出现收不到消息的情况。 在这里留下几个问题:
1、Looper.prepare()的作用是什么?
2、Looper.loop()的作用时什么?
3、为什么在主线程中没有使用这两个方法,程序正常运行?
大家稍安勿躁,带着这三个问题我们到源码中一探究竟。
源码解析
1、Handler构造方法。
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
------省去无用代码-----
//获取当前线程的Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//获取当前线程的Looper对象所对应的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从构造方法中我们可以看出创建Handler对象时做了两部分操作:1、默认为当前线程创建了一个Looper对象;2、获取Looper对应的MessageQueue对象,将Handler对象和MessageQueue对象绑定到一起。
但是我们在代码中也看到一条报错信息与上文注意点1处的报错信息一致。由源码可以看出:注意点1处之所以报错,是因为在子线程中默认没有创建其对应的Looper对象,所以我们要手动调用Looper.prepare()
方法。
2、为什么主线程中没有调用Looper.prepare()方法而没有报错?
在Android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫UI线程)创建时,会自动调用ActivityThread的1个静态的main()方法,这个方法相当于是程序的入口。我们来看一下main方法。
public static void main(String[] args) {
//调用prepareMainLooper方法
Looper.prepareMainLooper();
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
}
public static void prepareMainLooper() {
//调用Looper.prepare()方法
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");
}
//调用Looper的构造方法
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
//将当前线程和MessageQueue进行绑定
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
看到上面这段代码恍然大悟,在应用初始化时已经自行调用了Looper.prepareMainLooper();
方法,为主线程创建了一个Looper对象,并将主线程和MessageQueue对象进行了绑定。
之前我们还说过Looper.prepare()
方法,其实和Looper.prepareMainLooper()
方法调用过程相同,只是当先的线程对象不同:一个是主线程,一个是子线程。有兴趣的小伙伴可以自行查阅。
3、Looper.loop()方法做了什么?
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.");
}
//2、得到Looper对象对应的MessageQueue
final MessageQueue queue = me.mQueue;
for (;;) {//死循环
Message msg = queue.next(); //查找消息队列中的下一条消息
if (msg == null) { //如果消息队列中没有消息,直接返回
return;
}
-------------------省去不必要代码-----------------
try {
//msg.target获取的是Handler对象。
//找到Message对应的Handler对象,调用dispatchMessage(msg)方法进行消息分发
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//对分发过的消息进行回收
msg.recycleUnchecked();
}
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//初始化消息对象时没有初始化callback字段
handleCallback(msg);
} else {
if (mCallback != null) {//如果使用的是post方法时,mCallback不为null;如果是sendMessage方法,则为null
if (mCallback.handleMessage(msg)) {
return;
}
}
//此处调用的handleMessage方法就是在创建Handler对象时重写的handleMessage方法
handleMessage(msg);
}
}
从上面的代码可以看出Looper.loop()方法的执行过程如下: 1、找到当前线程对应的Looper对象。 2、通过第一步获取的Looper对象,获取其绑定的MessageQueue对象。 3、轮询消息队列对消息进行分发。
小结
从上面三个步骤我们就可以解释之前留下来的三个问题:
1、Looper.prepare()的作用是什么?
这个方法中主要做了两件事:1、获取当前对象的Looper对象;2、将当前前程和MessageQueue绑定到一起。
2、Looper.loop()的作用时什么?
这个方法通过轮询当前线程的MessageQueue对象,将消息发送给对应的Handler对象。
3、为什么在主线程中没有使用这两个方法,程序正常运行?
通过ActivityThread->main()方法可以看出:在主线程中默认调用了Looper.prepareMainLooper()和Looper.loop()方法。
4、创建Message对象
//实例化Message对象,通过new或者obtain方法获取
Message message = Message.obtain();
message.what = 0; //初始化消息属性字段
message.obj = "线程1发来贺电";
public static Message obtain() {
//在消息内容存在一个消息池,可以将Message进行复用,从而加快对象的初始化速度。
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
//从源码中可以看出,官方推荐使用obtain方法进行Message对象的初始化,提高了对象的复用性。
//减少由于对象的创建而多占用内存。
}
//如果从消息池中没有去处消息,再通过new的方式创建Message对象。
return new Message();
}
5、在工作线程中发送消息:Handler.sendMessage(Message)
public final boolean sendMessage(Message msg){
//调用sendMessageDelayed(Message,long)方法
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
//调用sendMessageAtTime方法
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//获取MessageQueue对象
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//调用enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//将当前Handler对象赋值给Message.target字段。
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用MessageQueue对象的enqueueMessage方法
return queue.enqueueMessage(msg, uptimeMillis);
}
---------------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;
}
if (p == null || when == 0 || when < p.when) {
//如果消息队列中的消息为空,将消息放到消息队列的第一位
//如果当前消息处于阻塞状态,将对其进行唤醒
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); //native方法
}
}
return true;
}
发送消息流程总结如下:
1、调用Handler->sendMessage方法,通过sendMessageDelayed方法,最后会调用MessageQueue的enqueueMessage方法
2、在enqueueMessage方法中,首先会判断当前消息队列中是否存在消息。
如果不存在,将消息插入到第一位;
如果存在消息,则遍历整个队列,根据消息的创建时间进行插入。
最后根据之前的Looper.loop()方法不断轮询消息队列,从消息队列中取出消息,根据消息的target属性找到对应的Handler对象处理消息。 至此Handler机制已经基本上分析完毕,一些重要的说明在注释中已经表现。
总结
1、在主线程中创建Handler时,会默认调用Looper.prepareMainLooper()和Looper.loop()方法方法。这两个方法主要是给当前线程创建Looper对象、将当前线程绑定一个MessageQueue对象。
2、创建Message对象时将要发送的消息内容设置给Message对象的各个字段。
3、发送消息时,首先会为Message对象设置target属性,将其和对应的Handler对象进行绑定;然后会将Message对象放入MessageQueue中去,如果消息队列中没有消息,将要发送的消息放入到第一位;如果消息队列处于阻塞状态,需要将其唤醒。
4、通过Looper轮询消息队列,将消息发送给对应的target(Handler)对象进行处理。
下一篇文章将会从源码的角度分析“消息屏障”。最后,欢迎给位大佬批评指正。