handler机制属于面试常客啦,看懂handler源码有助于我们理解使用。网络上的源码解析也有很多,这篇是自己边看源码边记录的博客,加深印象。本篇将会从handler的使用流程出发,也是分享自己是如何看源码的,当然,平时我看源码就是先百度看其他大佬的源码解析,然后在自己看源码比较好,最后在自己总结回顾下。这最后一步很早就知道,只是之前并不知道他的重要性~
本文内容图解
handler机制无非就是讲解Handler,Looper,messageQueue,Message之间是如何协调工作的。
handler机制成员
Handler 发送/处理消息
MessageQueue 消息队列,存储消息
Looper 循环器,从消息队列取消息发送給handller
message 消息
先来张自制流程图感受下~^_^大概的流程
使用流程
private Handler hanlder = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
//发送消息方式1
Message message = Message.obtain();
message.what = 1;
message.obj = "sss";
hanlder.sendMessage(message);
//发送消息方式2
hanlder.post(new Runnable() {
@Override
public void run() {
}
});
handler的创建
点击Handler进去,如下图
public Handler(Callback callback) {
this(callback, false);
}
内部调用的是两个参数的,ctrl按住,点击this看看这个构造函数的代码。
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();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
第一个if代码块看不懂?没关系,看他的Log猜一下,应该是判断创建的log是否是static或者使用弱应用(一般Handler是内部类的时候都会这么提示一下,这是为了避免内存泄漏!)
Looper的创建
好嘞~现在看mLooper = Looper.myLooper();这个是得到looper对象,通过Looper.myLooper()得到。好奇的话可以点进去在瞅瞅,点击myLooper()进入看看说明。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
看上面英文说明,返回一个与当前线程关联的Looper对象,如果当前线程没有关联的Looper,就返回null。ok,点到即止,我们知道了mLooper = Looper.myLooper();是获取与当前线程关联的Looper对象了。回到上面的Handler构造函数中,往下看,判断mLooper是不是空的,是空的就抛出异常。抛出啥异常嘞?,看下面
Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()"
翻译就是说,当前线程还没调用Looper.prepare(),所以不能在当前线程创建Handler。
handler创建部分小结:
1、调用mLooper = Looper.myLooper()方法获取到与当前线程关联的looper对象,
2、看myLooper的源码得知,返回空就是当前线程没有looper对象啦。
3、如果当前线程没有Looper对象,就是myLooper返回空的时候,会抛出异常,说当前线程还没调用Looper.prepare()方法。
得出结论,要创建Handler对象必须先在当前线程得到looper对象,要得到与当前线程关联的looper对象,要先调用Looper.prepare();方法。
那么创建一个Handler对象首先构造函数中是获取了与当前线程关联的looper对象,往下看代码,还有获取了mQueue消息队列。
额外小结一:创建looper对象有2种方式:1.Looper.prepare();2.Looper.prepareMainLooper()这俩有啥不同呢?
prepare方法是创建与当前线程关联的looper对象。
prepareMainLooper是创建与UI线程(主线程)关联的looper对象
为什么UI线程没有调用prepare方法也能创建好一个Handler不报错?
因为在UI线程启动过程中,源码内部已经帮我们调用了Looper.prepareMainLooper()方法,所以不用我们自己在去创建了。
额外小结二:回顾上面Handler构造函数中是这样获取消息队列的。
mQueue = mLooper.mQueue;
MessageQueue消息队列怎么来的
那么消息队列在Looper中是如何拿到的呢?可以看到是调用已经获取到的mLooper对象中的mQueue。mLooper对象可以在prepare中创建。那就去prepare中看看。
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
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));
}
上面这段就是prepare的源码。说明翻译:在实际开始循环(消息队列循环)前,调用prepare()方法可以将当前线程初始化为一个looper对象,这可以让我们创建一个与looper关联的handler对象。注意下这里有一个可能抛出的异常,
Only one Looper may be created per thread
说明一个线程只有一个与之关联的looper!!!一对一关系!!!
重点是看下创建的代码哈,new Looper(quitAllowed),点击new Looper进去看看代码;
/**
* Return the {@link MessageQueue} object associated with the current
* thread. This must be called from a thread running a Looper, or a
* NullPointerException will be thrown.
*/
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
发现了啥,在Looper的构造函数中,直接创建了一个消息队列!!O(∩_∩)O哈哈~,吃个饭回来,差点把我搞蒙蔽了,如果你看到这里有点懵逼?慢慢看,理解下前面的逻辑,就比较清晰了。在创建looper对象时,顺带把messageQueue一起创建了。我们知道looper是与线程关联的,那么由looper创建的messageQueue也间接跟线程关联了。为啥这么说,看上面的代码中 myQueue方法。返回一个与当前线程关联的消息队列呀。。。
Looper.loop()方法
创建完Handler之后,我们需要调用mLooper.loop();方法。loop()方法很长,这里抽取部分代码
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
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
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
msg.recycleUnchecked();
}
}
抽取什么代码呢?抽点我能看得懂的哈哈哈,,,,
1、首先通过myLooper()方法获取到looper对象,
2、在通过该对象获取到消息队列queue。通过for死循环获取队列中的消息msg,
3、然后调用msg.target.dispatchMessage(msg)方法将msg分发出去。
大概就是这么个意思。简单来说loop()方法就是不断的循环分发消息。
dispatchMessage源码解析
这里msg.target是什么呢、我们点击Message去看看源码
/*package*/ Handler target;
啊哈~是一个Handler。也就是通过handler调用dispatchMessage(msg)方法。 我们找到Handler类,在里面找到dispatchMessage方法
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
简短的源码还好读嘞~说明翻译是在这里处理系统消息。
如果msg.callback不为空,就调用handleCallback(msg)方法,点击handlerCallback方法进去看看
private static void handleCallback(Message message) {
message.callback.run();
}
调用了massage.callback.run()方法,这里的callback我们也通过查看message的源码看看
/*package*/ Runnable callback;
是一个runnable对象!想到了啥?我们发送消息的方式是不是有2种:
1、handler.sendMessage(Message msg)
2、handler.post(Runnable r);
而第二种post是传入一个Runnable对象!
post方式发送消息
这里简单看看handler.post方法源码
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
post调用了sendMessageDelayed方法,上面英文说明翻译:调用该方法导致runnable r 会被添加进消息队列。这个runnable 将在handler依赖的线程上运行起来。(如果handler是主线程,runnable就会在主线程上跑,就这个意思。)
继续看sendMessageDelayed方法中先调用了getPostMessage方法。进去瞅瞅~
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到传入的是一个runnable对象,返回的是一个message对象!!代码中做了啥呢?首先创建一个message对象,对message对象的属性callback赋值传进来的参数r,返回该message。很简单,说白了就是把runnable对象用一个message包装下~返回上一段代码,post方法中调用了
sendMessageDelayed(getPostMessage(r), 0);
就是传入了一个Message对象和一个数字0,进入该方法看看,
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
这段代码读起来也是香甜可口,通俗易懂呀~(*^▽^*)。首先看代码内容就知道了,发送meg消息。就这件事,在看看英文说明,等待指定时候后,将该消息加入消息队列,(哦,原来sendMessageAtTime方法就是把msg加入到消息队列),你将会在消息队列依赖的线程中,在与之绑定的handler的方法HandlerMessage中遇到他。什么意思呢?把msg消息发送到消息队列,消息队列是绑定了某个线程吧~比如绑定了线程A,那么线程A中的Handler会重写handlerMessage方法,该方法中有个Message参数咯,就是这个msg啦。
小结一下回顾一下,我们刚刚分析了loop()方法,就是不断的获取消息,分发消息,那分发消息就是调用dispatchMessage()方法,在这个方法中我们先判断要分发的msg的callback属性是不是空的,不是空的就说明是这个消息是通过Handler.post()方法传入的消息队列的。
ok,回到dispatchMessage方法中,如果callback不为空,调用handleCallback方法
if (msg.callback != null) {
handleCallback(msg);
}
进入看看
private static void handleCallback(Message message) {
message.callback.run();
}
他回调了callback的run方法~就是执行了我们runnable中实现的run方法啦~\(^o^)/~
回来继续看dispatchMessage方法,重新贴一下代码
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果msg.callback为空,那我们去看看mCallback是不是空的。mCallback是handler的一个Callback变量。细心的朋友就知道在哪里获取啦,上面代码有贴过哦~~
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();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
看倒数第三行~~是滴~我们在Handler创建的时候可以传入一个callback。本文的第一段代码,创建handler的方式就是传入了一个callback。如果handler的mCallback不为空,那就调用mCallback的handlerMessage即可。然后直接return掉~
在回头看看dispathMessage方法,
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
前面的都解释了,现在看看,如果msg的callback为空,并且mCallbaak也为空,那就调用handleMesage方法。这个方法是handler内部的方法。
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
可以看到是一个空方法,并且英文说明:子类必须实现这个方法来接收消息!我们在代码中如果这么写,就会调用这个方法啦:
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
但是这种方法存在内存泄漏问题,一般不这么些~看看这么写会有啥提示
看到这段代码想到了啥?上面博主有写到哈~ 一般Handler是内部类的时候都会这么提示一下,这是为了避免内存泄漏!
好嘞~分发消息到handler之后就是handleMesssage处理消息了,之后流程就结束啦,是不是很简答~
最后看下消息的发送方式。其实上面提到了。
//发送消息方式1
Message message = Message.obtain();
message.what = 1;
message.obj = "sss";
hanlder.sendMessage(message);
//发送消息方式2
hanlder.post(new Runnable() {
@Override
public void run() {
}
});
消息发送分为sendMessage和post两种。当然sendMessage和post还可以再细分
首先我们看sendMessage方式
我们通过Message.obtain方式创建消息。这是推荐的创建Message对象的方式,点击ontain看下源码啦
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*
*/
public static Message obtain() {
synchronized (sPoolSync) {
//sPool是一个静态类Messag,
if (sPool != null) {
//spool指向下一个message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
//看变量名字,猜:这个全局池是有数量上限的,每增加一个池的数量就减1
sPoolSize--;
//返回的是原来spool,可以看到并没有去new 一个message,减少内存开销
return m;
}
}
//spool为空就new一个
return new Message();
}
英文说明:为了避免创建太多实例,内部是有一个全局池,从全局池返回一个message对象。
post方式上面也提到啦,就是发送一个runnable对象,内部是将其封装为一个message对象然后添加到消息队列~
全文小结~
1、首先,我们在创建Handler前,要先调用Looper.prepare()方法,调用该方法创建与当前线程关联的loop对象,并且创建了消息队列。
2、然后我们才可以创建Handler对象,handler构造函数默认获取与当前线程关联的loop对象和消息队列,由此handler间接关联了当前线程,(额外小点:内存泄漏的原因:handler间接持有activity的引用,导致activity无法关闭,内存无法释放导致内存泄漏。)
3、调用looper.loop循环取出消息队列消息,dispatchMessage发送給各个Handler处理。
4、发送的方式分为2种,sendMessage和post方法。
补充:
1、消息队列不是队列,是一个单链表
2、消息是如何被准确的发送到对应的handler中去处理的?
回答:回顾源码就知道,消息队列取出消息后,在loop()循环中取出消息后调用: msg.target.dispatchMessage(msg);。
这句话就是讲代码发送到指定的handler去处理,这里的target就是具体的handler对象。所以只要找到这个target是什么时候被赋值的,就能回答这个问题了。这里举个栗子~比如handler.sendMessage(new Message());
我们去sendMessage中查看:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
发现他回调:sendMessageDelayed()方法,那就在进去看看
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
回调sendMessageAtTime方法,继续看
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);
}
到这里看到方法的英文名应该就知道了,消息队列入队,继续点进去看看
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
终于到了,msg.target = this,是吧, 这里的this就是handler对象了。即是我们调用handler.sendMessage中的handler了。
3、在loop中执行死循环为什么不造成ANR:
目前我看了众多文章,我的理解:首先我们要理解ANR的概念,application not response,造成ANR的原因(条件):1.事件未处理,可能被阻塞了,没有机会处理。2、在处理了,但是规定事件内还没完成。比如:activity中5秒内没完成处理,brocast中10s内没完成,service20s内没完成都会造成anr。
那么在此内完成任务即便是死循环anr也ok,因为没达到anr条件。loop中是可能造成anr的。
4、loop()中执行了死循环,那主线程的生命周期是如何被调用的?
首先我们需要明白一点,主线程不是唯一的线程,当需要的时候,由其他线程通过handler发送消息,让handler处理指定的线程生命周期。
5、loop()死循环会消耗大量资源吗?
不会,消息队列中没有消息就会阻塞,阻塞时处于休眠状态,并不会大量消耗资源。
ok~更多更仔细的回答,可以看看:https://www.zhihu.com/question/34652589
最后再次看下本文内容图解: