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资源。