Android Handler的原理机制与问题避免

(整期优先)网络出版时间:2021-02-26
/ 2

Android Handler的原理机制与问题避免

吕超峰

(张家界航空工业职业技术学院 湖南张家界 427000)

摘要:Android程序分为主(UI)线程和子线程。为了避免一系列耗时操作影响UI线程导致页面卡顿,Android提供Handler工具类来解决此类问题。然而在使用过程中,Android学习者与开发者在使用handler时并不了解其机制,导致出现内存泄漏、消息同步等问题。本文旨在通过分析Handler的原理机制,为学习者和开发者在使用Handler出现问题时提出解决方案。

关键字:Android,Handler,内存泄漏,消息同步

0 引言

Android操作系统是目前最为流行的手机操作系统之一。在Android的应用程序中,为了保证对用户操作界面的及时响应性。Android主线程(UI线程)主要负责UI绘制,耗时操作不能放在主线程执行,否则容易造成卡顿,严重时会ANR(应用无法响应)。比如与网络服务器进行数据交互、文件下载等服务器交互操作或者是本地的文件读取操作,都会将其置于子线程当中。而如何使得子线程和UI线程进行通信则显得尤为重要。因此,Android提供了Handler来作为异步线程进行通信的工具。这种方式也是Android官方推荐的方式,比较节省性能。在Android中最常使用Handler的场景就是主线程调用子线程去进行网络访问,子线程在获取到网络访问的返回结果并处理数据之后,通知主线程去更新UI。例如下拉刷新、上拉加载以及硬盘数据读取(IO操作)。Handler在这里充当的是消息的发送者和接收者的角色。所谓消息,也就是某种特定条件发生之后的状态。例如获取完网络数据之后,获取数据成功或者失败,就是不同的状态,需要发送不同的消息。

1 Handler的机制

Message(消息):需要被传递的消息。分为硬件产生的消息(比如按钮、用户触摸)以及软件生成的消息。

MessageQueue(消息队列):负责消息的存储与管理,通过将Handler发送过来的Message存放于消息队列中,然后依次将其取出来。读取以后则会自动删除消息,通过单链表进行维护,以方便其进行插入和删除。通过next()方法进行无限循环,从而能够判断出是否有消息,如果有则将消息传递出去并且进行删除。

Handler(消息处理器):负责Message的发送以及处理。主要通过Handler.sendMessage()向消息池发送各种消息事件。以及Handler.handleMessge()来进行消息处理。处理的过程将会按照FIFO(先进先出)原则进行消息的执行,其内部同样使用的是单链表的结构。

  1. Handler的使用问题

2.1 内存泄漏

内存泄漏问题是Handler使用过程中的第一大问题[1]。因为内部类会有一个指向外部类的引用(这个Handler又持有Activity的引用,就导致该Activity无法被回收)。垃圾回收机制中约定,当内存中的一个对象的引用计数为0时,将会被回收。而Handler作为Android上的异步消息处理机制,它的工作是需要Looper和MessageQueue配合的。简单的说,要维护一个循环体(Looper)处理消息队列(MessageQueue)。每循环一次就从MessageQueue中取出一个Message,然后回调相应的消息处理函数。如果,循环体中有消息未处理(Message排队中),那么Handler会一直存在,那么Handler的外部类(通常是Activity)的引用计数一直不会是0,所以那个外部类就不能被垃圾回收。因此大多数的Android初学者和开发者经常会遇到activity的onDestroy方法一直不执行导致内存泄漏。

基于以上原因,我们可以采用软引用和弱引用来解决Handler导致的内存泄漏问题。软引用(SoftReference):如果一个对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以一直被程序使用。弱引用(WeakReference):如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。两者之间根本区别在于:只具有弱引用的对象拥有更短暂的生命周期,可能随时被回收。而只具有软引用的对象只有当内存不够的时候才被回收,在内存足够的时候,通常不被回收。

通过以上分析我们可以使用使用静态内部类来创建Handler,但是与此同时Handler就不能调用Activity里的非静态方法了,所以加上「弱引用持有外部Activity。即可解决内存泄漏问题。

2.2 消息同步问题

其次是Handler的同步屏障机制

[2]。如果有一个紧急的Message需要优先处理,该怎么处理呢?这其实涉及到架构方面的设计了,通用场景和特殊场景的设计。我们可能会想到sendMessageAtFrontOfQueue()这个方法,实际也远远不只是如此,Handler中加入了同步屏障这种机制,来实现异步消息优先执行的功能。postSyncBarrier()发送同步屏障,removeSyncBarrier()移除同步屏障。同步屏障的作用可以理解成拦截同步消息的执行,主线程的Looper会一直循环调用 MessageQueue的next()来取出队头的Message执行,当Message执行完后再去取下一个。当next()方法在取Message时发现队头是一个同步屏障的消息时,就会去遍历整个队列,只寻找设置了异步标志的消息,如果有找到异步消息,那么就取出这个异步消息来执行,否则就让next()方法陷入阻塞状态。如果next()方法陷入阻塞状态,那么主线程此时就是处于空闲状态的,也就是没在干任何事。所以,如果队头是一个同步屏障的消息的话,那么在它后面的所有同步消息就都被拦截住了,直到这个同步屏障消息被移除出队列,否则主线程就一直不会去处理同步屏幕后面的同步消息。而所有消息默认都是同步消息,只有手动设置了异步标志,这个消息才会是异步消息。因为主线程中如果有太多消息要执行,而这些消息又是根据时间戳进行排序,如果不加一个同步屏障的话,那么遍历绘制View树的工作就可能被迫延迟执行,因为它也需要排队,那么就有可能出现当一帧都快结束的时候才开始计算屏幕数据,那即使这次的计算少于16.6ms,也同样会造成丢帧现象。

3. 总结

Handler是构成整个Android系统的基础,本文通过对Handler的原理进行剖析,分析出在使用Handler的过程中会出现的问题,以及提出解决问题所应采取的措施。以期解决Android学习和开发过程中对Handler使用所产生的问题。


参考文献

[1]基于Android系统的《国际功能、残疾和健康分类·康复组合》移动APP的开发[J]. 章马兰,燕铁斌. 中国康复医学杂志. 2019(02)

基于动态插桩的C/C++内存泄漏检测工具的设计与实现[J]. 曾佳平,杨秋辉,汪华龙,徐保平,黄蔚. 计算机应用研究. 2015(06)


作者简介:吕超峰 1991.07.20 男 汉 湖南省邵阳县 本科 助教 软件专业