阿里P7Android高级架构进阶视频(内含Handler视频讲解)免费学习请点击:https://space.bilibili.com/474380680
一、Handler
在Android开发的过程中,我们常常会将耗时的一些操作放在子线程(work thread)中去执行,然后将执行的结果告诉UI线程(main thread),熟悉Android的朋友都知道,UI的更新只能通过Main thread来进行。那么这里就涉及到了如何将
子线程的数据传递给main thread呢?
Android已经为我们提供了一个消息传递的机制——Handler,来帮助我们将子线程的数据传递给主线程,其实,当熟悉了Handler的原理之后我们知道,Handler不仅仅能将子线程的数据传递给主线程,它能实现任意两个线程的数据传递。
Handler的原理及其使用
1、Handler最常规的使用方式:
文章的开头说过,Handler不仅仅是能过将子线程的数据发送给主线程,它适用于任意两个线程之间的通信。
2、两个子线程之间通信
在一个线程创建Handler,另外一个线程通过持有该Handler的引用调用sendMessage发送消息。
代码如下:
接下来我们看下,为什么在子线程里实例化的时候不调用Looper.prepare()就会报错呢?
了解到此,我们已经成功引出了Handler机制几个关键的对象了,Looper、MessageQueue、Message。
为什么在主线程中创建Handler不需要要用Looper.prepare()和Looper.loop()方法呢?
其实不是这样的,App初始化的时候都会执行ActivityThread的main方法,我们可以看看ActivityThread的main()方法都做了什么!
我们接着来看Handler发送消息的过程:
sendMessage的关键在于enqueueMessage(),其内部调用了messageQueue的enqueueMessage方法
Message的发送实际是放入到了Handler对应线程的MessageQueue中,那么,Message又是如何被取出来的呢?
细心的朋友可能早早就发现了,之前抛出异常的地方讲解了半天的Loop.prepare()方法,一直没有说到Loop.loop()方法。同时,在之前的例子中也看到了,如果不调用Looper.loop()方法,Handler是接受不到消息的,所以我们可以大胆的猜测,消息的获取肯定和它脱不了关系!当然关怀疑还不行,我们还必须找出真相来证明我们的猜想?那还等什么,先看看loop()方法吧。
到这里,想必你应该清楚如何在不同的线程之间来使用Handler了吧。
2 总结
1、在使用handler的时候,在handler所创建的线程需要维护一个唯一的Looper对象, 每个线程对应一个Looper,每个线程的Looper通过ThreadLocal来保证,如需了解ThreadLocal,点击查看详细讲解 ,Looper对象的内部又维护有唯一的一个MessageQueue,所以一个线程可以有多个handler,但是只能有一个Looper和一个MessageQueue。
2、Message在MessageQueue不是通过一个列表来存储的,而是将传入的Message存入到了上一个Message的next中,在取出的时候通过顶部的Message就能按放入的顺序依次取出Message。
3、Looper对象通过loop()方法开启了一个死循环,不断地从looper内的MessageQueue中取出Message,然后通过handler将消息分发传回handler所在的线程。
最后附上一张自己理解画出来的流程图:
3 Handler补充:
1. Handler在使用过程中,需要注意的问题之一便是内存泄漏问题。
为什么会出现内存泄漏问题呢?
首先Handler使用是用来进行线程间通信的,所以新开启的线程是会持有Handler引用的,如果在Activity等中创建Handler,并且是非静态内部类的形式,就有可能造成内存泄漏。非静态内部类是会隐式持有外部类的引用,所以当其他线程持有了该Handler,线程没有被销毁,则意味着Activity会一直被Handler持有引用而无法导致回收。同时,MessageQueue中如果存在未处理完的Message,Message的target也是对Activity等的持有引用,也会造成内存泄漏。
解决的办法:
(1). 使用静态内部类+弱引用的方式:
静态内部类不会持有外部类的的引用,当需要引用外部类相关操作时,可以通过弱引用还获取到外部类相关操作,弱引用是不会造成对象该回收回收不掉的问题,不清楚的可以查阅JAVA的几种引用方式的详细说明。
(2). 在外部类对象被销毁时,将MessageQueue中的消息清空。例如,在Activity的onDestroy时将消息清空。
2. 在使用Handler时,通常是通过Handler.obtainMessage()来获取Message对象的,而其内部调用的是Message.obtain()方法,那么问题来了,为什么不直接new一个Message,而是通过Message的静态方法obtain()来得到的呢?
下面就通过代码来一探究竟
其实在在Message中有一个static Message变量sPool,这个变量是用于缓存Message对象的,在obtain中可以看到当需要一个Message对象时,如果sPool不为空则会返回当前sPool(Message),而将sPool指向了之前sPool的next对象,(之前讲MessageQueue时讲过Message的存储是以链式的形式存储的,通过Message的next指向下一个Message,这里就是返回了sPool当前这个Message,然后sPool重新指向了其下一个Message),然后将返回的Message的next指向置为空(断开链表),sPoolSize记录了当前缓存的Message的数量,如果sPool为空,则没有缓存的Message,则需要创建一个新的Message(new Message)。
接着看下sPool中缓存的Message是哪里来的?
recycle()是回收Message的方法,在Message处理完或者清空Message等时会调用。
recycleUnchecked()方法中可以看到,将what、arg1、arg2、object等都重置了值,如果当前sPool(Message缓存池)的大小小于允许缓存的Message最大数量时,将要回收的Message的next指向sPool,将sPool指向了回收的Message对象(即将Message放到了sPool缓存池的头部)
小结:
由此可见,使用obtain获取Message对象是因为Message内部维护了一个数据缓存池,回收的Message不会被立马销毁,而是放入了缓存池,
在获取Message时会先从缓存池中去获取,缓存池为null才会去创建新的Message。
3. Handler sendMessage原理解读。
引入问题!
sendMessageDelayed是如何实现延时发送消息的?
sendMessageDelayed是通过阻塞来达到了延时发送消息的结果,那么会不会阻塞新添加的Message?
阿里P7Android高级架构进阶视频(内含Handler视频讲解)免费学习请点击:https://space.bilibili.com/474380680
原文链接:https://blog.csdn.net/wsq_tomato/article/details/80301851