Handler的作用
Handler的作用:在子线程更新UI或者处理延时任务
一般来说:我们不能在子线程更新UI(其实这么描述并不准确),这个大家应该都知道
那如果要在子线程更新UI,有好几种写法可以做到
但是,其实内部也是通过Handler发送消息到主线程做到更新UI
如果看了系统源码就会发现
Android整个底层架构都是基于Handler机制,包括Activity的生命周期回调,ANR机制等等
由此可见Handler机制的重要性
Handler是如何做到在主线程和子线程间通信的呢?
Handler如何执行的
一句话概括:
Looper不断从MessageQueue中获取Message,然后由Handler来处理Message
设计到的类如下:
Handler:处理Message
MessageQueue:消息队列
Looper:消息循环
Message:消息
Handler.sendMessage()就是把消息加入到消息队列
方法的调用过程:
Handler.sendMessage -> sendMessageDelyed ->sendMessageAtTime-> MessageQueue.enqueueMessage
// Handler sendMessage源码
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 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);
}
//发送消息最后都是走到这个方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
把消息加入到消息队列并对消息执行顺序排序,根据when字段
MessageQueue.enqueueMessage()
MessageQueue 数据结构是链表,增删Message 较快
// MessageQueue源码
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
Message p = mMessages;
boolean needWake;
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
handleMessage 消息的处理
1.拦截消息,如果Message的callback!= null
2.拦截消息,如果Handler的mCallback!= null,且handleMessage返回true
3.只有这两个判断都不成立的时候才会走Handler的handleMessage方法
dispatchMessage是系统来调用的,我们使用Handle的时候都是重写的handleMessage方法
这里可以看到,到真正处理消息的时候,前面还有两处判断可以打断,很多开源框架就在这做文章
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 1. msg 可以拦截消息
handleCallback(msg);
} else {
if (mCallback != null) {
// 2.handler 可以拦截消息
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
// ...省略很多代码...
// Callback是Handler的内部类
public interface Callback {
public boolean handleMessage(Message msg);
}
带着疑问看源码
问题1:子线程是可以创建Handler吗?
一般我们都是在主线程创建Handler,但是我们可以试试看
在子线程创建Handler,会报错,那就表示不能创建了吗?
其实是可以的,只是需要先调用 Looper.prepare()
然后调用Looper.loop()
new Thread(new Runnable() {
@Override
public void run() {
//Looper.prepare();
//子线程创建Handler报错
Handler handler = new Handler();
//Looper.loop();
}
}).start();
//错误信息
/**Handler构造方法
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}*/
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:203)
at android.os.Handler.<init>(Handler.java:117)
这个时候可能有人会问:
但是主线程中创建Handler的时候,我并没有调用Looper.prepare()啊
怎么不报错呢?
你没调用,不代表就没有调用啦!
是因为系统在应用启动的入口
ActivityThread类的main()方法中帮我们调用了
// APP启动入口
public static void main(String[] args) {
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
// Looper部分源码
// Looper的构造方法
private Looper(boolean quitAllowed) {
// 创建Looper的时候,创建了消息队列,并保存了当前所在的线程
// 主线程消息队列的quitAllowed是false,表示主线程的MessageQueue不允许quit
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
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
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
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
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));
}
问题2:如何保证一个线程只有一个Looper
Looper源码, ThreadLocal保证一个线程中只有一个Looper对象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
/**
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
*/
// ThreadLocal 其实都可以单独拿出来说了
// Handler是如何做到一个线程只有一份Looper和消息队列的呢
// Thread有个属性 ThreadLocal.ThreadLocalMap threadLocals = null;
// ThreadLocalMap 是 ThreadLocal<T>内部类 保存的是键值对键是 ThreadLocal ,值是泛型类型的值
// ThreadLocalMap 是线程独有的
sThreadLocal.set(new Looper(quitAllowed));
}
// 循环分发消息
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
总结:
- 系统在应用启动的入口,ActivityThread的main()方法中调用了 Looper.prepareMainLooper()
创建了主线程的Looper和消息队列(Looper构造方法中创建了消息队列) - Looper.loop()启动了消息循环,通过消息绑定的Handler来处理消息
// Message 源码
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
msg.target.dispatchMessage(msg)
问题3:主线程死循环为啥不卡死?
上面有看到ActivityThread类的源码main()方法
可以看出最后两行代码
Looper.loop()开启循环后,就抛异常了
换句话说,如果执行了后面的抛异常的语句,那就玩完了
可见正常情况下应该走不到最后一行代码
那么就会一直在 Looper.loop()中
这就奇怪了,那么需要主线程一直在死循环中才行
循环执行完的话就GG了
所以问题:主线程死循环为啥不卡死是个矛盾?
在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的
nativePollOnce()方法里此时主线程会释放CPU资源进入休眠状态,
直到下个消息到达或者有事务发生,
通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,
可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,
本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源
参考链接:https://www.zhihu.com/question/34652589/answer/90344494
补充:IdleHandler
IdleHandler是MessageQueue的内部类,应该很少有人关注到这个类
看翻译就知道大概作用了
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*/
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
MessageQueue是会阻塞的
Looper里面Message暂时执行完毕了就会回调IdleHandler
具体作用就不啰嗦了,可以参考文章:
https://mp.weixin.qq.com/s/KpeBqIEYeOzt_frANoGuSg