Android之Handler浅析

Android之Handler浅析

Handler相信每一个从事Android开发的小伙伴都很是熟悉了, 最经常使用的场景就是在子线程中进行数据操做而后经过handler消息机制通知到UI线程来更新UI,地球人都知道在子线程中更新UI,通常状况下都会报错。往往出去面试被问到“handler原理”,“消息是怎么从子线程发送到主线程的”等等handler底层的实现,就懵逼了。面试

虽然网上关于分析handler的博客问丈夫很是多,已经有不少大佬分析的很是清晰了。这里分析主要是为了让本身加深理解,另外一方面就是想分享所学知识。框架

Android消息循环流程图以下所示:ide

主要涉及的角色以下所示:oop

  • Message:消息。
  • MessageQueue: 消息队列,负责消息的存储于管理,负责管理由handler发过来的Message,读取会自动删除消息,单链表维护,插入和删除上有优点。 在其next()方法中会无限循环,不短判断是否有消息,有就返回这条消息并移除。
  • Looper: 消息循环器,负责关联线程以及消息的分发,在该线程下从MessageQueue获取Message,分发给handler,Looper建立的时候会建立一个MessageQueue,调用loop()方法的时候消息循环开始,其中会不断调用MessageQueue的next()方法,有消息就处理,不然就阻塞在MessageQueue的next()方法中,当Looper的quit()被调用的时候会调用MessageQueue的quit(),此时的next()会返回null,而后loop()方法也就跟着退出。
  • Handler:消息处理器,负责发送并处理消息,面向开发则,提供API,并隐藏背后实现的细节。

整个消息循环流程仍是比较清晰的,具体来讲:post

  • 1.Handler经过sendMessage()发送消息Message到队列MessageQueue.
  • 2.Looper经过loop()不断提取触发条件的Message,并将Message交给对应的target handler来处理。
  • 3.target handler调用自身的handleMessage()方法来处理Message。

事实上,在整个消息循环的流程中,并不止只有Java层参与,不少重要的工做都是在C++层来完成,咱们来看看下图的这些类的调用它关系。ui

注:虚线表示关联关系,实现表示调用关系。this

在这些类中MessageQueue 是Java层与C++层维系的桥梁,MessageQueue与Looper相关功能都经过MessageQueue的Native方法来完成,而其余虚线链接的类只有关联关系,并无直接调用的关系,它们发生关系的桥梁是MessageQueue.spa

总结:插件

  • Handler发送的消息由MessageQueue存储管理,并由Looper负责回调消息到handlerMessage()
  • 线程的切换由Looper完成,handlerMessage()所在线程由Looper.loop()调用者所在线程决定。

下面来列举咱们几个工做中或许都会遇到的问题。

Handler引发的内存泄漏缘由以及最佳解决方案

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();
}

为何咱们能在主线程直接使用Handler,而不须要建立Looper?

一般咱们认为ActivityThread就是主线程,事实上它并非一个线程,而是主线程操做的管理者。在ActivityThread.main()方法中调用了Looper.prepareMainLooper()方法建立了主线程的looper,而且调用了loop()方法,全部咱们就能够直接使用Handler了。

所以咱们能够利用Callback这个拦截机制来拦截Handler的消息,如大部分插件化框架中的Hook ActivityThread.mH的处理。

主线程的Looper不容许退出

主线程不容许退出,退出就意味APP要挂了。

Handler里藏着Callback能干什么?

Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;若是 Callback 处理了消息,可是并无拦截,那么就意味着一个消息能够同时被 Callback 以及 Handler 处理。

建立Message实例的最佳方式

为了节省开销,Android给Message设计了回收机制,因此咱们在使用的时候尽可能复用Message,减小内存的消耗:

  • 经过Message的静态方法Message.obtain()
  • 经过Handler的共有方法handler.obtainMessage()

子线程里弹Toast的正确姿式

本质上是由于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();

妙用Looper机制

  • 将Runnable post 到主线程执行
  • 利用Looper判断当前线程是不是主线程
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();
    }

}

主线程的死循环一直运行是否是特别消耗CPU资源呢?

并非,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,经过往pipe管道写端写入数据来唤醒主线程工做。这里采用的epoll机制,是一种IO多路复用机制,能够同时监控多个描述符,当某个描述符就绪(读或写就绪),则马上通知相应程序进行读或写操做,本质是同步I/O,即读写是阻塞的。因此说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

相关文章
相关标签/搜索