前言
Handler大家想必已经非常熟悉,在安卓中用于线程通信,而且在面试中也会经常谈及,今天小盆友就在此聊聊Handler,加强下印象,也方便日后忘记时可查看。
目录:
1、简析Handler
2、如何达到通信
3、简单手写Handler
1、简析Handler
Handler构成
- Message:Handler接收和处理的消息对象。
- MessageQueue:消息队列。从名字上看,便知道他是使用先进先出(FIFO)的形式管理Message。
- Loop:用于从MessageQueue中读取Message,然后交给发送该消息的Handler处理。在Message类中有一个“Handler target”属性,用于保存发送该消息的Handler,也是处理该Message的Handler。值得注意的是一个线程只能有一个Looper。
- Handler:用于处理和发送消息。
浅谈Handler源码
当我们使用Handler发送一个消息(Message)时,Handler会将发送的消息时,假设此处调用了sendMessage方法进行发送,在Handler源码会经过sendMessageDelayed方法,然后再经过sendMessageAtTime方法,最后进入enqueueMessage方法,会在这个方法中,将这条消息中的target(Handler类型)属性置为当前的Handler,源码如下,在注释1⃣️处进行设置。处理消息的时候会拿取消息的这个target,然后将消息交由target处理,稍后的分析会有调用的地方,这里先有个印象,方便后面讲解。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //1⃣️
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//2⃣️
}
经过设置之后,会将该消息加入MessageQueue中,即上面代码段的注释2⃣️处,值得注意的是queue就是我们说的消息队列,而这个队列是哪来的呢,或是说由谁实例化的呢。且看下面的一段Handler的构造函数,Handler的构造函数比较多,但是如果其参数不含有Looper类型参数的构造函数,最终都调用到下面这函数。显然,从注释3⃣️可知Handler的Looper通过Looper.myLooper()获取,而消息队列是从该Looper中获取(请看注释4⃣️),进而“猜测”(因为目前我们还没看到new的字样)消息队列是由Looper进行维护,但需要一探究竟,进入至Looper中。
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper(); //3⃣️
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //4⃣️
mCallback = callback;
mAsynchronous = async;
}
进到Looper类中,其构造方法中发现消息队列的实例化语句(注释5⃣️),由此解决刚才在Handler中使用的消息队列是由谁来创建的问题。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //5⃣️
mThread = Thread.currentThread();
}
这里值得注意的是Looper的构造函数只有一个而且是private,所以这里必有蹊跷,追溯其使用的地方,便看到了如下的一段代码。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");//6⃣️
}
sThreadLocal.set(new Looper(quitAllowed));//7⃣️
}
想必大家非常熟悉prepare这个方法,在子线程中使用Handler都需要先调用下Looper.prepare(),而主线程不用。这是因为主线程在ActivityThread的main方法中已经通过Looper.prepareMainLooper()开启,而子线程需要自行调用,以此来初始化属于自己线程的Looper(注释7⃣️),将其保存在一个ThreadLocal类型的变量(即注释7⃣️的sThreadLocal)中,这个sThreadLocal变量便是达到Handler切换线程的核心所在,因为在注释3⃣️的Looper.myLooper()获取到的便是ThreadLocal保存该线程的Looper对象(注释8⃣️)。
略微一提的是,如果已调用该线程的Looper.prepare()再次调用的话,便会看到我们熟悉的注释6⃣️的异常。
public static @Nullable Looper myLooper() {
return sThreadLocal.get(); //8⃣️
}
ThreadLocal简单的说便是一个能够维护该线程数据的一个类型,各自线程只能获取到其各自线程的数据。具体介绍请各自google或百度,这里就不扩展了。
现在回过头来,Handler发送了Message,而后Message被添加至消息队列中。此时,我们会在子线程的代码最后调用了一句Looper.loop(),其代码如下,loop方法会运行一个死循环,每次循环注释9⃣️处会从消息队列中获取一个Message,这里有可能会阻塞(当消息队列中没消息时,会进行阻塞),取到消息由谁处理,就由Message持有的target来决定(请看注释🔟),这里的便呼应了我们最开始的说法(Message中target设置为发送他的Handler)。而且值得注意的是,注释🔟的dispatchMessage方法,最后会调用到我们在创建Handler中重写的handleMessage,这样便解释清楚了,整个Handler消息机制的主体流程。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...//省略一些代码
for (;;) {
Message msg = queue.next(); // might block 9⃣️
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...//省略一些代码
try {
msg.target.dispatchMessage(msg); // 🔟
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...//省略一些代码
}
}
小结
我们再次理清下Handler使用过程的逻辑:
1、Looper.prepare() 进行初始化Looper和Looper中的MessageQueue属性,然后ThreadLocal保存该Looper对象,同时MessageQueue是Looper的一个属性,所以也可以说一起被保存进ThreadLocal中。
2、new Handler(){...} handler被创建时,会获取创建其对象的线程的Looper和Looper中的MessageQueue,handler用于当调用sendMessage时,将Message添加到该线程的MessageQueue中。
3、Looper.loop() 开启死循环获取MessageQueue中的消息,然后交由Handler处理。
2、如何达到通信
从第一小节中,其实我们已经讲的很清楚Handler是如何达到线程间的通信的,但因为小盆友最近面试常被问到这个问题,所以觉得有必要多啰嗦几句。
这里我们就使用子线程切换回主线程(UI线程)的例子来说。
Looper被创建后,便存进了ThreadLocal,而ThreadLocal是保存当前线程的变量的一个类型。在Handler机制中,所有需要Looper的地方,都是通过Looper的静态方法myLooper来进行获取,从而得到当前线程的Looper,而后子线程想更新主线程的话,都会对主线程new出来的handler进行发送Message,因为这个handler对象内持有的便是主线程的Looper,所以当Message加入该Looper的MessageQueue之后,当从MessageQueue取出的Message时就已经是在主线程了。所以,达到切换线程的第一功应该给ThreadLocal。
3、简单手写handler
根据第一小节的分析,我们手写一个handler也是需要这四个类,各自实现的功能如下:
- Handler:处理和接受消息
- Looper:启动、退出和执行
- Message:保存消息信息
- MessageQueue:消息进队、消息出队、退出
这里的逻辑和源码分析大致类似,就不再赘述,直接上代码,再次申明这里只是简单的模拟handler,作为更好的自我理解而已。
/**
* @author Jiang zinc
* @date 创建时间:2018/2/4
* @description
*/
public class Handler {
//需要向looper的消息队列中放消息
private final MessageQueue mQueue;
public Handler() {
//获取当前线程的looper
Looper looper = Looper.myLooper();
mQueue = looper.mQueue;
}
public void sendMessage(Message message){
message.handler = this;
mQueue.enqueueMessage(message);
}
public void handleMessage(Message message) {
}
}
/**
* @author Jiang zinc
* @date 创建时间:2018/2/4
* @description
*/
public class Looper {
//用于存放在该线程中的looper,确保一条线程只有一个looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
//消息队列,用于存放消息,让looper循环从中获取
MessageQueue mQueue;
private Looper() {
mQueue = new MessageQueue();
}
/**
*
* @date 创建时间 2018/2/4
* @author Jiang zinc
* @Description 准备looper
* @version
*
*/
public static void prepare(){
if(null != sThreadLocal.get()){
throw new RuntimeException(Thread.currentThread()+"已经有looper");
}
//如果没有就new一个looper
sThreadLocal.set(new Looper());
}
/**
*
* @date 创建时间 2018/2/4
* @author Jiang zinc
* @Description 获取当前线程looper
* @version
*
*/
public static Looper myLooper(){
return sThreadLocal.get();
}
/**
*
* @date 创建时间 2018/2/4
* @author Jiang zinc
* @Description 用于循环获取消息
* @version
*
*/
public static void loop(){
//获取当前线程的looper
Looper looper = Looper.myLooper();
//当前线程的消息队列
MessageQueue queue = looper.mQueue;
for(;;){
//获取message
Message next = queue.next();
//next为null,退出循环
if(null == next){
break;
}
//分发到发送message的handler 执行
next.handler.handleMessage(next);
}
}
public void quit() {
mQueue.quit();
}
}
/**
* @author Jiang zinc
* @date 创建时间:2018/2/4
* @description
*/
public class Message {
//消息标记
int what;
//消息体
Object object;
//下一条消息(类似链表)
Message next;
//发送消息的handler,需要分发到这个handler处理
Handler handler;
//资源回收
public void recycle(){
object = null;
next = null;
handler = null;
}
}
/**
* @author Jiang zinc
* @date 创建时间:2018/2/4
* @description
*/
public class MessageQueue {
//消息链表
Message mMessage;
private boolean isQuit;
/**
* @date 创建时间 2018/2/4
* @author Jiang zinc
* @Description 将消息放入队列
* @version
*/
public void enqueueMessage(Message message) {
synchronized (this) { //因为有存有取,需要进行同步
if(isQuit){
return;
}
Message p = mMessage;
if (null == p) { //p为空,说明该消息队列已没有消息,放入的消息直接作为链头
mMessage = message;
} else {
Message prev;
//通过循环查找链表尾端,将消息放置链表尾
//链表尾条件:当前message的next为null,即为链表尾
for (; ; ) {
prev = p;
p = p.next; //此时p为prev的下一条消息
if (null == p) { //p为空,说明prev为链表尾
break;
}
}
prev.next = message;
}
//通知message 解除阻塞
notify();
}
}
/**
* @date 创建时间 2018/2/4
* @author Jiang zinc
* @Description 获取messageQueue的消息
* @version
*/
public Message next() {
synchronized (this) {
Message message;
for (; ; ) {
if(isQuit){
return null;
}
message = mMessage;
if (null != message) {
break;
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//将消息的表头往后移
mMessage = mMessage.next;
return message;
}
}
public void quit() {
synchronized (this) {
isQuit = true;
Message message = this.mMessage;
while (null != message) {
//获取下一个message
Message next = message.next;
message.recycle();
//将message指向下一个
message = next;
}
notify();
}
}
}
最后进行调用:
/**
* @author Jiang zinc
* @date 创建时间:2018/2/4
* @description
*/
public class Main {
public static void main(String []args){
Looper.prepare();
final Handler handler = new Handler(){
@Override
public void handleMessage(Message message) {
System.out.println(Thread.currentThread()+" recv:"+message.object);
}
};
new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread()+" send");
Message message = new Message();
message.object = "Zinc Power";
handler.sendMessage(message);
}
}.start();
final Looper looper = Looper.myLooper();
new Thread(){
@Override
public void run() {
try {
sleep(1_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
looper.quit();
}
}.start();
Looper.loop();
}
}

写在最后
至此,本片文章就完结了,撒花。如果喜欢的话,给个心吧。
网友评论