Handler 老生常谈的问题了,非常建议看一下Handler 的源码。刚入行的时候,大佬们就说 阅读源码 是进步很快的方式。
Handler的基本原理 Handler 的 重要组成部分
Message 消息 MessageQueue 消息队列 Lopper 负责处理MessageQueue中的消息
消息是如何添加到队列的 对照着上面的大的逻辑图,我们深入一下,看一下,一个消息 是如何被发送到 MessageQueue 又是如何被 Lopper 处理的
handler 发送一个message 的方法如下图所示
而这些方法最终都会执行 Handler 中的 enqueueMessage 方法,我们看一下 enqueueMessage 方法做了什么
1 2 3 4 5 6 7 private boolean enqueueMessage (@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { return queue.enqueueMessage(msg, uptimeMillis); }
消息队列如何将消息排序 MessageQueue 收到 消息以后,会根据时间进行排列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 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; prev.next = msg; } if (needWake) { nativeWake(mPtr); } } return true ; }
Handler的消息队列在哪创建的 回到创建Handler的地方,他的构造方法
1 2 3 4 public Handler () { this (null , false ); }
1 2 3 4 5 6 7 8 9 10 public Handler (Callback callback, boolean async) { mLooper = Looper.myLooper(); mQueue = mLooper.mQueue; }
1 2 3 4 5 6 7 8 final MessageQueue mQueue;private Looper (boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); }
可以看到 Handler其实是拿着Looper 的MessageQueue当做自己的MessageQueue
Loope有什么作用 消息被有序的添加到了消息队列中,而Looper就是负责将消息从消息队列中取出。当执行Looper的loop()方法,Looper会从消息队列中取出消息,然后交给handler的dispatchMessage去处理消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void loop () { for (;;) { Message msg = queue.next(); try { msg.target.dispatchMessage(msg); } catch (Exception exception) { } } }
一个线程有几个Looper 要想知道有几个Lopper,肯定要先知道Looper在哪里创建。Looper有一个prepare方法
1 2 3 4 public static void prepare () { prepare(true ); }
在这里会创建一个新的Looper 并且设置到了ThreadLocal
1 2 3 4 5 6 7 8 9 10 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)); }
在ThreadLocal可以看到是以map的形式去保存,保证了一个线程只有一个map,又将looper和ThreadLocal进行绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void set (T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null ) map.set(this , value); else createMap(t, value); }
看到Looper中的 sThreadLocal
1 2 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
他是一个静态的 final 保证了 一个Looper只有一个 sThreadLocal
最终保证了,一个线程只有一个Looper
主线程什么时候执行preapre 想要使用Looper,肯定需要先prepare 去创建一个Looper,那么主线程如何创建Looper的呢?我们知道 java 程序的入口是 main 方法, 对于Android来说,其实也有一个main 方法,他的位置在 ActivityThread
1 2 3 4 5 6 7 8 9 10 11 public static void main (String[] args) { Looper.prepareMainLooper(); Looper.loop(); }
Handler内存泄露 Handler为什么会有可能导致内存泄露? 我们知道 内部类会持有外部类的引用,当我们做一个延时任务,延时10S,然后在10S内退出Activity,在我们sendMessage的时候,handler对象被传递给msg 如👇所示,然后被存放在MessageQueue中。在这10S内,即使Activity销毁了,但是引用关系依然被保存在MessageQueue中,那么即使Activity销毁了,他的对象依然不会被GC销毁,因为他依然被引用。就导致内存未被回收。
1 2 3 4 5 6 7 private boolean enqueueMessage (@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this ; }
那么如何处理Handler内存泄露呢
1.将Handler改成静态类。原因是因为静态类不会持有外部类的引用 2.继承Handler,将Activity作为弱引用使用 3.在界面退出的时候,调用Handler的removeMessages方法
消息队列没有消息时Handler如何挂起 Looper从MessageQueue中获取message,当获取不到message的时候,会将 nextPollTimeoutMillis置成-1,然后进入下次循环,当执行nativePollOnce方法时候,如果nextPollTimeoutMillis==-1那么就会执行Linux的epoll机制,让线程处于挂起状态,阻塞线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Message next () { for (;;) { nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this ) { if (msg != null ) { } else { nextPollTimeoutMillis = -1 ; } if (pendingIdleHandlerCount <= 0 ) { mBlocked = true ; continue ; } } } }
1 2 3 4 5 6 7 public static void loop () { for (;;) { Message msg = queue.next(); } }
Handler如何退出 使用looper去执行quit方法退出
1 2 3 4 5 6 7 public void quit () { mQueue.quit(false ); } public void quitSafely () { mQueue.quit(true ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void quit (boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit." ); } synchronized (this ) { if (mQuitting) { return ; } mQuitting = true ; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } nativeWake(mPtr); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 Message next () { for (;;) { nativePollOnce(ptr, nextPollTimeoutMillis); if (mQuitting) { dispose(); return null ; } } }
1 2 3 4 5 6 7 8 9 10 11 public static void loop () { for (;;) { Message msg = queue.next(); if (msg == null ) { return ; } } }
总结
Looper会先将消息队列中的消息全部清空,然后使用nativeWake的native方法唤醒线程,在上面我们介绍了,当消息队列中没有消息的时候,线程会挂起,处于等待状态,当我们唤醒以后,Looper的loop方法会继续执行下去,然后从MessageQueue中获取到一个null的Message,最终将Looper的loop()方法退出
主线程能够Quit么? 我们知道了 主线程是在ActivityThread的main方法中执行了Looper.prepareMainLooper()
创建的Looper
1 2 3 4 5 6 @Deprecated public static void prepareMainLooper () { prepare(false ); }
1 2 3 4 5 private static void prepare (boolean quitAllowed) { sThreadLocal.set(new Looper(quitAllowed)); }
1 2 3 4 5 6 private Looper (boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
1 2 3 4 5 MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; }
1 2 3 4 5 6 7 void quit (boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit." ); } }
回头在看一下 Looper的prepare方法,只有主线程可以创建一个不可以quit的MessageQueue,其他线程创建的都是可以quit的
1 2 3 4 5 6 7 8 9 10 11 12 public static void prepare () { prepare(true ); } private static void prepare (boolean quitAllowed) public static void prepareMainLooper () { prepare(false ); }
为什么设计主线程不能被quit 在ActivityThread中,定义了一个H的类,继承了Handler,这个H的handler执行了Android所有的主要事件,比如广播,service,Activity生命周期等都是在这里进行处理,所以不能把主线程quit
1 2 3 4 class H extends Handler {}
消息如何知道是由哪个Handler发送的? 一个线程可以有多个Handler,想new几个都可以,在我们往MessageQueue中添加消息的时候,会加入一个target标记是哪个handler发送的
1 2 3 4 5 6 7 private boolean enqueueMessage (@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void loop () { for (;;) { try { msg.target.dispatchMessage(msg); } catch (Exception exception) { } }
Handler如何确保线程安全的 1 2 3 4 5 6 boolean enqueueMessage (Message msg, long when) { synchronized (this ) { } }
1 2 3 4 5 6 7 8 Message next () { for (;;) { synchronized (this ) { } } }
Message如何复用的 看一下我们quit的时候,是怎么从消息队列中清空消息的
1 2 3 4 5 6 7 8 9 10 11 void quit (boolean safe) { synchronized (this ) { if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } } }
1 2 3 4 5 6 7 8 9 10 11 private void removeAllMessagesLocked () { Message p = mMessages; while (p != null ) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void recycleUnchecked () { flags = FLAG_IN_USE; what = 0 ; arg1 = 0 ; arg2 = 0 ; obj = null ; replyTo = null ; sendingUid = UID_NONE; workSourceUid = UID_NONE; when = 0 ; target = null ; callback = null ; data = null ; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this ; sPoolSize++; } } }
使用obtain方法会从之前清空状态的链表中取出一个Message去使用,减少创建Message带来的内存消耗。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static Message obtain () { synchronized (sPoolSync) { if (sPool != null ) { Message m = sPool; sPool = m.next; m.next = null ; m.flags = 0 ; sPoolSize--; return m; } } return new Message(); }
这种设计模式称为享元设计模式
为什么主线程loop不会导致ANR 首先要知道ANR是怎么出现的,ANR出现的条件有两个
5秒内没有响应输入的事件,触摸反馈等 广播10秒内没有执行完毕 在上面我们分析知道,所有的事件都是由Handler进行分发,在主线程上,发送一个事件,这个事件耗时,将主线程的loop()给卡主,让他只能执行当前任务,不能去处理其他事件就出现了ANR
ANR的本质是由于不能及时处理消息导致的,和他的loop是没有任何关系的
Handler同步屏障 同步屏障概念 啥叫同步屏障,字面意思,就是阻挡同步消息,那么Handler同步屏障是干啥的,没错,你没听错,就是阻挡同步消息,让异步消息过去。阻挡同步消息 这就是同步屏障
在发送消息的时候,mAsynchronous 控制着是否发送的消息是否为异步消息
1 2 3 4 5 6 7 8 9 10 11 12 private boolean enqueueMessage (@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this ; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true ); } return queue.enqueueMessage(msg, uptimeMillis); }
在Handler构造方法中,控制则是否是异步消息。但是这个方法是hide,正常我们是不能调用的
1 2 3 4 5 6 @hide public Handler (@Nullable Callback callback, boolean async) { mAsynchronous = async; }
开启同步屏障 那么如何开启同步屏障呢,MessageQueue 中提供了一个 postSyncBarrier 方法 开启同步屏障,
1 2 3 4 public int postSyncBarrier () { return postSyncBarrier(SystemClock.uptimeMillis()); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 private int postSyncBarrier (long when) { synchronized (this ) { final int token = mNextBarrierToken++; final Message msg = Message.obtain(); msg.markInUse(); msg.when = when; msg.arg1 = token; Message prev = null ; Message p = mMessages; if (when != 0 ) { while (p != null && p.when <= when) { prev = p; p = p.next; } } if (prev != null ) { msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } return token; } }
同步屏障工作原理 开启以后,同步屏障如何将异步消息传递出去,将同步消息阻挡下来呢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Message next () { if (msg != null && msg.target == null ) { do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null ) { if (now < msg.when) { nextPollTimeoutMillis = (int ) Math.min(msg.when - now, Integer.MAX_VALUE); } else { mBlocked = false ; if (prevMsg != null ) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null ; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } }
取消同步屏障 取消同步屏障以后,会唤醒线程,去处理之前未被处理的同步消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public void removeSyncBarrier (int token) { synchronized (this ) { Message prev = null ; Message p = mMessages; while (p != null && (p.target != null || p.arg1 != token)) { prev = p; p = p.next; } if (p == null ) { throw new IllegalStateException("The specified message queue synchronization " + " barrier token has not been posted or has already been removed." ); } final boolean needWake; if (prev != null ) { prev.next = p.next; needWake = false ; } else { mMessages = p.next; needWake = mMessages == null || mMessages.target != null ; } p.recycleUnchecked(); if (needWake && !mQuitting) { nativeWake(mPtr); } } }
GIF演示 下面以一个简单的示例更佳直观的表现,示例分成3中情况
没有启动同步屏障,发送同步消息 发送异步消息 开启同步屏障,发送同步消息 发送异步消息 开启同步屏障,发送同步消息 发送异步消息 在取消同步屏障 没有启动同步屏障,发送同步消息 发送异步消息
可以看到,如果不开启同步屏障,对于Handler 来说 消息都是会被发送出去
开启同步屏障,发送同步消息 发送异步消息
通过对比能够发现,当开启同步屏障以后,发送的同步消息并没有打印,只有异步消息打印了,说明同步屏障确实只能够允许异步消息通过
开启同步屏障,发送同步消息 发送异步消息 在取消同步屏障
当我们移除同步屏障以后,之前没有收到的同步消息,会立马同步过来
演示代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 class HandlerAct : AppCompatActivity() { companion object { const val TAG = "handler-tag" const val MESSAGE_TYPE_SYNC = 0x01 const val MESSAGE_TYPE_ASYN = 0x02 } private var index = 0 private lateinit var handler :Handler private var token: Int? = null @RequiresApi (Build.VERSION_CODES.M) override fun onCreate (savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_handler) initHandler() linear.addView(MaterialButton(this ).apply { text = "插入同步屏障" setOnClickListener { sendSyncBarrier() } }) linear.addView(MaterialButton(this ).apply { text = "移除屏障" setOnClickListener { removeSyncBarrier() } }) linear.addView(MaterialButton(this ).apply { text = "发送同步消息" setOnClickListener { sendSyncMessage() } }) linear.addView(MaterialButton(this ).apply { text = "发送异步消息" setOnClickListener { sendAsynMessage() } }) } private fun initHandler () { Thread { Looper.prepare() handler = Handler(){ when(it.what){ MESSAGE_TYPE_SYNC -> { Log.i(TAG, "收到同步消息<========== index:${it.arg1}" ) } MESSAGE_TYPE_ASYN -> { Log.i(TAG, "收到异步消息<========== index:${it.arg1}" ) } } true } Looper.loop() }.start() } private fun sendSyncMessage () { index++ Log.i(TAG, "插入同步消息==========> index:$index" ) val message = Message.obtain() message.what = MESSAGE_TYPE_SYNC message.arg1 = index handler.sendMessageDelayed(message, 1000 ) } @RequiresApi (api = Build.VERSION_CODES.LOLLIPOP_MR1) private fun sendAsynMessage () { index++ Log.i(TAG, "插入异步消息==========> index:$index" ) val message = Message.obtain() message.what = MESSAGE_TYPE_ASYN message.arg1 = index message.isAsynchronous = true handler.sendMessageDelayed(message, 1000 ) } @RequiresApi (Build.VERSION_CODES.M) @SuppressLint ("DiscouragedPrivateApi" ) fun sendSyncBarrier () { try { Log.d(TAG, "插入同步屏障" ) val queue: MessageQueue = handler.looper.queue val method: Method = MessageQueue::class.java.getDeclaredMethod("postSyncBarrier") method.isAccessible = true token = method.invoke(queue) as Int Log.d(TAG, "token:$token" ) } catch (e: Exception) { e.printStackTrace() } } @SuppressLint ("DiscouragedPrivateApi" ) @RequiresApi (api = Build.VERSION_CODES.M) fun removeSyncBarrier () { Log.i(TAG, "移除屏障" ) try { val queue: MessageQueue = handler.looper.queue val method: Method = MessageQueue::class .java .getDeclaredMethod ( "removeSyncBarrier", Int::class .javaPrimitiveType ) method .isAccessible = true method.invoke(queue, token) } catch (e: Exception) { e.printStackTrace() } } }