Handler相信每一個從事Android開發的小夥伴都很是熟悉了, 最經常使用的場景就是在子線程中進行數據操做而後經過handler消息機制通知到UI線程來更新UI,地球人都知道在子線程中更新UI,通常狀況下都會報錯。往往出去面試被問到「handler原理」,「消息是怎麼從子線程發送到主線程的」等等handler底層的實現,就懵逼了。面試
雖然網上關於分析handler的博客問丈夫很是多,已經有不少大佬分析的很是清晰了。這裏分析主要是爲了讓本身加深理解,另外一方面就是想分享所學知識。框架
Android消息循環流程圖以下所示:ide
主要涉及的角色以下所示:oop
整個消息循環流程仍是比較清晰的,具體來講:post
事實上,在整個消息循環的流程中,並不止只有Java層參與,不少重要的工做都是在C++層來完成,咱們來看看下圖的這些類的調用它關係。ui
注:虛線表示關聯關係,實現表示調用關係。this
在這些類中MessageQueue 是Java層與C++層維繫的橋樑,MessageQueue與Looper相關功能都經過MessageQueue的Native方法來完成,而其餘虛線鏈接的類只有關聯關係,並無直接調用的關係,它們發生關係的橋樑是MessageQueue.spa
總結:插件
Handler容許咱們發送延遲消息,若是在延時期間內用戶關閉了activity,那麼該activity會泄漏。這個泄漏是由於Message會出油Handler,而又由於Java的特性,內部類會持有外部類,使得activity會被Handler持有, 這樣最終就會致使activity泄漏了。線程
解決的辦法就是:將Handler定義爲靜態的內部類,在內部持有activity的弱引用,並在activity的ondestroy()中調用handler.removeCallbacksAndMessage(null)及時移除全部消息。
private static class SafeHandler extends Handler { private WeakReference<HandlerActivity> ref; public SafeHandler(HandlerActivity activity) { this.ref = new WeakReference(activity); } @Override public void handleMessage(final Message msg) { HandlerActivity activity = ref.get(); if (activity != null) { activity.handleMessage(msg); } } }
而且再在 Activity.onDestroy() 前移除消息,加一層保障:
@Override protected void onDestroy() { safeHandler.removeCallbacksAndMessages(null); super.onDestroy(); }
一般咱們認爲ActivityThread就是主線程,事實上它並非一個線程,而是主線程操做的管理者。在ActivityThread.main()方法中調用了Looper.prepareMainLooper()方法建立了主線程的looper,而且調用了loop()方法,全部咱們就能夠直接使用Handler了。
所以咱們能夠利用Callback這個攔截機制來攔截Handler的消息,如大部分插件化框架中的Hook ActivityThread.mH的處理。
主線程不容許退出,退出就意味APP要掛了。
Handler.Callback 有優先處理消息的權利 ,當一條消息被 Callback 處理並攔截(返回 true),那麼 Handler 的 handleMessage(msg) 方法就不會被調用了;若是 Callback 處理了消息,可是並無攔截,那麼就意味着一個消息能夠同時被 Callback 以及 Handler 處理。
爲了節省開銷,Android給Message設計了回收機制,因此咱們在使用的時候儘可能複用Message,減小內存的消耗:
本質上是由於Toast的實現依賴於Handler,按子線程使用Handler的要求修改便可,同理的還有就是dialog。
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Toast.makeText(MainActivity.this, "不會崩潰啦!", Toast.LENGTH_SHORT).show(); Looper.loop(); } }).start();
public final class MainThread { private MainThread() { } private static final Handler HANDLER = new Handler(Looper.getMainLooper()); public static void run(@NonNull Runnable runnable) { if (isMainThread()) { runnable.run(); }else{ HANDLER.post(runnable); } } public static boolean isMainThread() { return Looper.myLooper() == Looper.getMainLooper(); } }
並非,這裏就涉及到Linux pipe/epoll機制,簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法裏,此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,經過往pipe管道寫端寫入數據來喚醒主線程工做。這裏採用的epoll機制,是一種IO多路複用機制,能夠同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則馬上通知相應程序進行讀或寫操做,本質是同步I/O,即讀寫是阻塞的。因此說,主線程大多數時候都是處於休眠狀態,並不會消耗大量CPU資源。