你知道android的MessageQueue.IdleHandler吗?
WeTest 导读
干货!干货!或许可以是一种处理问题的新思路哟!
前言
我们知道android是基于Looper消息循环的系统,我们通过Handler向Looper包含的MessageQueue投递Message, 不过我们常见的用法是这样吧?
一般我们比较少接触MessageQueue, 其实它内部的IdleHandler接口有很多有趣的用法,首先看看它的定义:
简而言之,就是在looper里面的message暂时处理完了,这个时候会回调这个接口,返回false,那么就会移除它,返回true就会在下次message处理完了的时候继续回调,让我们看看它有哪些有趣的用法吧~~
一、提供一个android没有的声明周期回调时机
如果有这种需求,想要在某个activity绘制完成去做一些事情,那这个时机是什么时候呢?有同学可能觉得onResume()是一个合适的机会,不是可是这个onResume() 真的是各种绘制都已经完成才回调的吗?No, too naive ~~
你看谷老师说了,onStart是用户可见,onResume是用户可交互,谷老师可没说onResume是绘制完成吧~那么android那些耗时的measure, layout, draw是在什么时候执行的呢?它们跟onResume()又有何关系呢?让我们先来看看源码吧~
1. ActivityThread.java
我们知道app的进程其实是ActivityThread, 那么activity的生命周期自然是它来执行了,
performResumeActivity就是回调onResume了, 我们继续看wm.addView方法, 这个ViewManager是一个接口,其实现者是WindowManagerImpl
2.WindowManagerImpl.java
这个mGlobal是WindowManagerGlobal对象,我们继续
3.WindowManagerGlobal.java
这里我们new 出了ViewRootImpl对象, 我们知道这个对象就是android view的根对象了,负责view绘制的measure, layout, draw的巨长的方法 performTraversals就是这个类的,我们继续看setView方法
4.ViewRootImpl.java
这个函数调用了关键方法requestLayout(), 我们继续跟踪,顺便说下,后面一连串的BadTokenException就是我们常常遇到的dialog相关抛出的,也有些特殊场景也会出这个异常,可以到这里查看线索。
调用了scheduleTraversals, 从名字就能看出来了吧:
它往Choreographer里面post了一个runnable, 这个Choreographer是android负责帧率刷新相关的东西,我们暂时可以不关注它,可以理解为往主线程post一个消息是一样的,顺便说下这个Choreographer可以做帧率检测相关的东西,,可以用于卡顿检测什么的···
我们看这个runnable果然是去执行了那个巨长无比的函数performTraversals函数, 现在我们可以总结下流程了:
结论:所以如果我们想在界面绘制出来后做点什么,那么在onResume里面显然是不合适的,它先于measure等流程了, 有人可能会说在onResume里面post一个runnable可以吗?还是不行,因为那样就会变成这个样子
所以你的行为一样会在绘制之前执行,这个时候我们的主角IdleHandler就发挥作用了,我们前面说了,它是在looper里面message暂时执行完毕了就会回调,顾名思义嘛,Idle就是队列为空的意思,那么我们的onResume和measure, layout, draw都是一个个message的话,这个IdleHandler就提供了一个它们都执行完毕的回调了,大概就是这样
说了这么多,那么现在获取到这个时机有什么用呢? look!!
这个是我们地图的公交详情页面, 进入之后产品要求左边的页卡需要展示,可以看到左边的页卡是一个非常复杂的布局,那么进入之后的效果可以明显看到头部的展示信息是先显示空白再100毫秒左右之后才展示出来的,原因就是这个页卡的内容比较复杂,用数据向它填充的时候花了较长时间,代码如下:
可以看到这个detailView就是这个侧滑的页卡了,填充里面的数据花了90ms,如果这个时间是用在了界面view绘制之前的话,就会出现以上的效果了,view先是白的,再出现,这样就体验不好了,如果我们把它放到IdleHandler里面呢?代码如下:
效果是这样的:
看出不同了吗?顶部的页卡先展示出来了,这样体验是不是会更好一些呢。虽然只有短短90ms,不过我们做app也应该关注这种细节优化的,是吧~ 这个做法也提供了一种思路,android本身提供的activity框架和fragment框架并没有提供绘制完成的回调,如果我们自己实现一个框架,就可以使用这个IdleHandler来实现一个onRenderFinished这种回调了。
二、可以结合HandlerThread, 用于单线程消息通知器
我们先思考一个问题,如果有一个model数据管理模块,怎么设计?比如地图的收藏模块的model部分。就是下面这个图的小星星:
它原来的model设计大概是这个样子的:
由于这个model是单例的,而且是多线程可以访问的,所以它的增删改查都加上了锁,而且由于外部访问需要遍历有哪些收藏点,所以外部遍历列表也需要加锁,大概是这样的:
因为是多线程可访问的,如果遍历不加锁的话,其他线程删除了一个收藏,就会crash的,原来的这样设计有几个不好的地方:
1. 外部使用者需要关系锁的使用,增加了负担,不用还不安全
2. 如果在主线程加锁的话,可能另一个线程执行操作会阻塞主线程造成anr
总之,多线程代码就是容易出错,而且真的出错的时候查起来太费劲了,目前收藏夹模块就有N多bug,所以我想用单线程来解决这个问题,由于model层的访问需要数据库和网络等,所以需要异步线程,那么单线程队列+异步线程,首先想到的就是HandlerThread, 大概架构如下:
现在,我们把原来多线程的逻辑改到了单线程里面,各种收藏的model共用一个HandlerThread,这样我们增删改查都不用加锁了,出错几率大大减小,而且这种model的设计有点类似插件的意思,可以很方便的增加其他收藏。
Ok, 那么跟我们的主题IdleHandler有什么关系呢?思考这样一个问题,地图上的小星星需要实时更新,也就是model的任何变化都需要显示到地图上,那么收藏的小星星就应该作为model的观察者,以前的做法是向收藏model注册监听,在每一个增删改查操作后都对观察者回调,大概是这样:
这样有一个小小的问题,就是如果有一个操作生成10个快速连续的增删改查操作,那么我们的UI就会收到10次回调,而这种场景下我们其实只需要最后一次回调就够了,中间操作其实不用刷新UI的。
那么现在改成单线程模型,我们又该如何处理这个问题呢?当然我们也能在每个post到异步线程的runnable里面去回调观察者,但这样未免不够优雅,所以这个时候IdleHandler不就又可以发挥作用了吗?它是在消息暂时处理完的时候回调的呀,不是很符合我们的时机么,对吧?
就是这个样子了,这里为什么不用第一个场景下的Looper.myQueue().addIdleHandler()呢?注意这个地方Looper.myQueue()如果在主线程调用就会使用主线程looper了,所以我选择反射这个HandlerThread的looper来设置它,这个IdleHandler我们返回了true, 表示我们要长期监听消息队列,因为返回false,下次就没有回调了哦。
好了,结论是这个地方IdleHandler用作了一个消息的触发器,是不是挺有意思的呢?
三、 结语
如果你没有用过它,从今天开始试试吧,这篇文章只是我个人的一点小思路,说不定这个IdleHandler有很多其他的用法呢~~
腾讯WeTest提供上千台真实手机,随时随地进行测试,保障应用/手游品质。节省百万硬件费用,加速敏捷研发流程。
同时腾讯WeTest兼容性测试团队积累了10年的手游测试经验,旨在通过制定针对性的测试方案,精准选取目标机型,执行专业、完整的测试用例,来提前发现游戏版本的兼容性问题,针对性地做出修正和优化,来保障手游产品的质量。目前该团队已经支持所有腾讯在研和运营的手游项目。
欢迎进入:http://wetest.qq.com/product/cloudphone体验安卓真机
欢迎进入:http://wetest.qq.com/product/expert-compatibility-testing使用专家兼容测试服务。WeTest兼容性测试团队期待与您交流!You Create,We Test!
如果对使用当中有任何疑问,欢迎联系腾讯WeTest企业QQ:800024531
你知道android的MessageQueue.IdleHandler吗?的更多相关文章
- Android Handler MessageQueue Looper 消息机制原理
提到Android里的消息机制,便会提到Message.Handler.Looper.MessageQueue这四个类,我先简单介绍以下这4个类 之间的爱恨情仇. Message 消息的封装类,里边存 ...
- [Android]Message,MessageQueue,Looper,Handler详解+实例
转http://www.eoeandroid.com/forum-viewthread-tid-49595-highlight-looper.html 一.几个关键概念 1.MessageQueue: ...
- Android之MessageQueue、Looper、Handler与消息循环
在android的activity中有各种各样的事件,而这些事件最终是转换为消息来处理的.android中的消息系统涉及到: * 消息发送 * 消息队列 * 消息循环 * 消息分发 * 消息 ...
- Android 中 MessageQueue 的 nativePollOnce
Android SDK 中的事件循环已经是一个老生常谈的问题了, 像 Handler Looper MessageQueue 这几个类也是被大家研究透彻了. 但是再回头看以前自己的分析, 总感觉差点什 ...
- android handler messageQueue,looper
韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 处理器获取 当前线程中的 循环器对象, 循环器 从 消息队列中 取出 消息, 给 处理器 ...
- Android消息处理机制(Handler、Looper、MessageQueue与Message)
Android是消息驱动的,实现消息驱动有几个要素: 消息的表示:Message 消息队列:MessageQueue 消息循环,用于循环取出消息进行处理:Looper 消息处理,消息循环从消息队列中取 ...
- (转)Android消息处理机制(Handler、Looper、MessageQueue与Message)
转自 http://www.cnblogs.com/angeldevil/p/3340644.html Android消息处理机制(Handler.Looper.MessageQueue与Messag ...
- Android多线程分析之四:MessageQueue的实现
Android多线程分析之四:MessageQueue的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前面两篇文章<Androi ...
- Android消息机制:Looper,MessageQueue,Message与handler
Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Mes ...
随机推荐
- JAVA编程入门
java最早是由Sun公司基于C++开发而成的新一代编程语言也是现行下的主流行编程语言,其原始的主要用于嵌入式开发.java的第一个版本为JDK1.0,到2017年已经升级到JAK1.9版本.java ...
- hashlib模块--摘要算法
算法介绍: Python的hashlib提供了常见的摘要算法:MD5,SHA()等. 摘要算法,又称哈希算法,散列算法.通过一个函数,吧任意长度的字符串转换为固定长度的字符串(16进制) 摘要算法就是 ...
- java如何调用接口方式一
java如何调用接口 其实对于java调用接口进行获取对方服务器的数据在开发中特别常见,然而一些常用的基础的知识总是掌握不牢,让人容易忘记,写下来闲的时候看看,比回想总会好一些. 总体而言,一些东西知 ...
- python-opencv aplpha混合
import cv2 import os import numpy as np print os.listdir(os.getcwd()) img = cv2.imread('building.jpg ...
- Maven 结合 IDEA 入门实践
一.Maven 基本安装 1. 下载 首先来到 http://maven.apache.org/download.cgi ,直接下载以 -bin.zip 结尾的文件,如图 2. 存储位置 将其解压后, ...
- 数据帧CRC32校验算法实现
本文设计思想采用明德扬至简设计法.由于本人项目需要进行光纤数据传输,为了保证通信质量要对数据进行校验.在校验算法中,最简单最成熟的非CRC校验莫属了. 得出一个数的CRC校验码还是比较简单的: 选定一 ...
- Azure 基础 : 使用 template 简化部署
笔者在前文中介绍了如何使用 PowerShell 脚本在 Azure 上创建虚拟主机.正如你所看到的,整个创建过程还是有点繁琐的,因为我们需要使用 PowerShell 脚本创建并关联所有相关的组件. ...
- HTTP Error 500.19 - Internal Server Error
1.使用svn对项目进行管理 2.之前都是平安无事,忽然有一天报错:HTTP Error 500.19 - Internal Server Error,如图: 3.经过各种挣扎和求证,最后发现是项目. ...
- MFC程序使用GTest搭建测试框架
一.起源 最近对单元测试比较感兴趣,之后就上网搜了一些测试的框架,C++项目使用的测试框架基本上都使用的GoogleTest,之后就开启了gtest的学习之路. 主要是根据<玩转Google开源 ...
- Tomcat请求头过大
今天开发反应Tomcat的请求头过大 <Connector port="8280" protocol="HTTP/1.1" connectionTimeo ...