Android之Handler浅析

比特觅虹鹤
• 阅读 2235

Android之Handler浅析

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

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

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

 Android之Handler浅析

主要涉及的角色如下所示:

  • 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,并隐藏背后实现的细节。

整个消息循环流程还是比较清晰的,具体来说:

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

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

 Android之Handler浅析

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

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

总结:

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

点赞
收藏
评论区
推荐文章
元旦 元旦
3年前
每日一问(一)Handler相关知识
1、Handler负责发送Message和处理Mesage2、Message就是消息载体,可用what区分,也可传递对象3、MessageQueue消息队列,存储Message4、Looper循环取出MessageQueue里的Message交给Handler处理。5、一个线程只有一个Looper和MessageQueue,子线程中使用Handler一
九路 九路
4年前
Android HandlerThread源码解析
在上一章Handler源码解析文章中,我们知道App的主线程通过Handler机制完成了一个线程的消息循环。那么我们自己也可以新建一个线程,在线程里面创建一个Looper,完成消息循环,可以做一些定时的任务或者写日志的功能。这就是HandlerThread的作用AndroidHandler消息机制源码解析(https://www.cnblogs.co
九路 九路
4年前
Android Handler消息机制源码解析
好记性不如烂笔头,今天来分析一下Handler的源码实现Handler机制是Android系统的基础,是多线程之间切换的基础。下面我们分析一下Handler的源码实现。Handler消息机制有4个类合作完成,分别是Handler,MessageQueue,Looper,MessageHandler:获取消息,发送消息,以及处理消息的类Mes
Stella981 Stella981
3年前
Android中AsyncTask的使用
原文https://blog.csdn.net/liuhe688/article/details/6532519在Android中实现异步任务机制有两种方式,Handler和AsyncTask。Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细
Stella981 Stella981
3年前
Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus
背景对于Android系统来说,消息传递是最基本的组件,每一个App内的不同页面,不同组件都在进行消息传递。消息传递既可以用于Android四大组件之间的通信,也可用于异步线程和主线程之间的通信。对于Android开发者来说,经常使用的消息传递方式有很多种,从最早使用的Handler、BroadcastReceiver、接口回调,到近几年流行的通
Stella981 Stella981
3年前
Android中的常见通信机制和Linux中的通信机制
HandlerHandler是Android系统中的一种消息传递机制,起作用是应对多线程场景。将A进程的消息传递给B线程,实现异步消息处理。很多情况是将工作线程中需要更新UI的操作消息传递给UI主线程,而实现更新UI操作。因为工作线程和主线程是共享地址空间,即Handler实例对象mHandler位于线程间共享的内存堆上,工作线程和主线
Stella981 Stella981
3年前
AsyncTask的用法
AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程..为什么需要使用异步任务?我们知道,Android中只有UI线程,也就是主线程才能进行对UI的更新操作,而其他线程是不能直接操作UI的.这样的好处是保证了UI的稳定性和准确性,避
Stella981 Stella981
3年前
Android 异步加载
一般会使用线程Thread、Timer或者使用AsyncTask,而这些操作都是在在后台另外开一个线程给我们找数据,具体得到的数据需要使用Handler去更新UI,AsyncTask也是一样使用到的Handler只是它将Handler封装在了onPostExecute执行操作中。使用过AsyncTask的同学都知道一个异步加载数据最少要
Stella981 Stella981
3年前
Handler更新UI的几种方式
Handler、loop、MessageQueue的工作原理Message:Handler接收和处理的消息对象Looper:每个线程只能拥有一个looper.它的loop方法负责读取MessageQueue中的消息,读到信息之后就把消息返回给handler处理MessageQueue:消息队列。程序创建Looper对象时,会在它的构造器中创建
Stella981 Stella981
3年前
Flutter学习笔记(31)
如需转载,请注明出处:Flutter学习笔记(31)异步更新UI(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fupwgh%2Fp%2F13100886.html)大家都知道,子线程不能操作UI控件,在我们Android的日常开发中,经常会遇
Stella981 Stella981
3年前
Android里面所说的Looper
Looper即:有消息循环的线程。在Android里线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新概念。主线程(UI线程)就是一个消息循环的线程。针对这种消息循环的机制,引入一个新的机制Handle,有消息循环,就要往消息循环里面发送相应的消息,自定义消息一般都会有对应的处理,消