写点什么

View 的异步 Inflate+ 全局缓存:加速你的页面

  • 2020-05-24
  • 本文字数:2389 字

    阅读完需:约 8 分钟

View的异步Inflate+全局缓存:加速你的页面

一、背景

Android 在 View 的使用中,过多的布局文件 inflate 影响性能,尤其在一些滚动列表、样式种类很丰富的场景下,inflate 次数相对较多,整体 inflate 耗时就会增加,导致滚动过程卡顿。


所以,需要 View 的异步 inflate,甚至 View 的全局缓存,通过这些方式,去减少 UI 线程 inflate 的耗时及次数,以便减少卡顿,提升性能。

二、现有的解决方案

要实现 View 的异步 Inflate,最简单粗暴的方式是直接 new Thread 执行 inflate,或者使用 HandleThread+Handler 的方式,或者使用 android 官方 android.support.v4.view 包的 AsyncLayoutInflater 类。但都存在一个问题,就是如果 inflate view 实例的时候 view 的构造方法里面直接有 new Handler()(原本想新建一个主线程 Handler 用于 UI 操作),将会达不到要求。


ListView、RecycleView 等控件,自身实现了“缓存复用”机制,这使得滑动过程性能得以提升,但更多的是控件间、或者页面内的缓存复用,而对于页面之间(activity 与 activity)缓存复用,没一个有效的解决方案。当然,如果把 View 的缓存做成全局单例,是可以做到页面之间的缓存复用,但 View 和 context 强绑定在一起,全局缓存可能会导致 activity 实例不能正常释放,导致内存泄露问题;也可能在 context 使用上存在问题,如用 APPlication 实例传递给 View 的 context,代码中又直接把 context 当成 activity 用,将导致一些问题。

三、异步 Infllate+全局缓存问题分析及解决方案

从现有方案中,提炼出两个核心问题,一、异步 Inflater 的时候,View 中 new Handler 导致的安全问题;二、全局缓存,View 的 context 问题。


  1. 异步 Inflater 的时候,View 中 new Handler 导致的安全问题解决方案


对于直接 new Thread 执行 inflate,或者使用 HandleThread+Handler 的方式,或者使用 android 官方 android.support.v4.view 包的 AsyncLayoutInflater 类。


示例代码



三种方式的对比:



第一种和第三种方式,run 方法中由于没有使用 Looper.loop 机制,这使得 new Handler 即使调用 post 方法发消息,并不会正在执行,导致 UI 不能正常刷新。


第二种,虽然 new Handler 能正常工作,但如刷新 UI,很可能会 crash,比如如下的异常:



所以 Handler hander = new Handler(),往 handler 抛的消息需要抛到主线程,比如改写成 Handler hander = new Handler(Looper.getMainLooper())。但是我们无法更改 View 的 Handler 构造代码,下面方案通过了反射的方式,强制把后台的线程的 Looper 设置为 mainLooper,这样后台线程 new Handler()方式也能把消息抛到主线程消息队列



使用示例:



  1. 全局缓存,View 的 context 问题解决方案


在全局缓存时,为了解决创建 view 的 context 不一定是 activity 导致的问题,或者是 activity 导致的内存泄露问题,对 Context 做封装:新建了 ViewContext 代理类:



inflate 的时候,将用 ViewContext 传入 View,方式如下:


四、基本实施及使用

  1. 基本架构



  1. 实施思路


1)View 的缓存大小应控制,且可动态修改:在 View 的缓存方面,设计一个缓存大小机制,且允许动态的修改对应的缓存大小,这样可以根据具体需要设置,从而更好的控制内存使用;


2)缓存 View 的状态处理,方便管理。异步创建 View,放入缓存池并标记可用,每次从缓存池获取 View 后,标记状态不可用,待回收后在标记可用;


3)异步创建 View,可预加载。提供 View 的异步创建,并放入缓存中,结合预加载,能有效的减少实际创建 View 所需的耗时,提升性能;


4)内存管理策略–应用低内存自动释放缓存。通过 context 取到 APPlication 注册一个 ComponentCallbacks,监听 APP 内存状态,适当的释放缓存;


5)缓存优先级策略–可设置缓存释放优先级。提供设置缓存释放优先级的能力,业务方可以更精准的利用缓存,更好的满足业务所需;


6)View 创建方式。对于 View 的创建,可以设计一个 IViewCreeator,创建 View 的过程由使用方决定。如布局文件中只有 TextView,可以传入 layoutId 选择 new TextView()的方式。


  1. 实施



基本使用如下,新建单例(如 ViewHelper):



使用 ViewHelper 如下:



使用前后的效果:



  1. 注意事项


1)使用异步 Inflate+全局缓存构建的 View,在使用时需要重新设置 LayoutParams,不然显示上可能不是最终想要的结果;


2)使用异步 Inflate+全局缓存构建的 View,如果 View 的解析过程中,存在 Theme 相关的,可能会导致 View 构建失败。如原生的 TabLayout,解析时会读取 Theme 中的属性,如果 context 传入的是 APPlication 且没有设置相关 Theme,就会报错;


3)使用异步 Inflate+全局缓存构建的 View,需要及时的调用 refreshCurrentActivity 方法,这样尽可能的保持 context 是当前的 activity 实例。在使用 context 的时候,避免直接把 context 强转 activity,而是使用 ViewContext 的 getActivity 方法获取。

五、总结


选择异步 Inflate ,应根据需要,合理的选择。如只需 activity 级别的,选择原生 AsyncLayoutInflater 的方式,就能很好的满足要求,并且没有 context 使用问题。如想更早的准备或者跨页面复用,View 的异步 Inflate+全局缓存是更好的选择,但要注意 context 使用问题,因为 inflate 所需的 context 不一定是 activity,也正是这点使得单例缓存的 View,不用担心内存泄露问题,满足多页面的缓存复用。


目前,优酷在 AsyncView 项目已经实现 View 的异步 Inflate+全局缓存,该项目已经对公司内部开源,是 AIOSO 的子项目之一,也在进一步的落实对外开源。它是“低侵入式”的,没有对 Android 原生 UI 进行改造,Android UI 框架开发的 APP 都可以方便接入。该项目已经在优酷 APP 上大量使用,反馈效果良好,主要体现在:


1)在帧率方面,整体带来了 5%左右提升。尤其在低端机,体感上有明显的提升;


2)在启动方面,结合各业务端提前做一些预加载任务,整体带来了 20%左右的提升。


作者 | 阿里文娱高级无线开发工程师 瑞源


2020-05-24 18:355813

评论 2 条评论

发布
用户头像
大佬有没有项目地址源码什么的牙
2023-05-30 18:51 · 重庆
回复
用户头像
forceSetMainLoop具体是什么原理呢?没看懂
2020-07-02 14:03
回复
没有更多了
发现更多内容

从零开始搭建MySQL主从复制架构

杨杰灵

Java MySQL 主从复制

绝无此例!用实例演示如何使用Spring搭建微服务框架

Java永远的神

spring 面试 微服务 springboot SpringCloud

破解数字化转型困局,企业分析协同场景案例解析

ModelWhale

数据分析 数字化转型 构建模型 成功案例 协同软件

2021年数据泄露成本报告解读

SEAL安全

DevSecOps 数据安全 混合云 零信任 软件供应链

豆瓣评分9.0,“Linux命令行”经典巨著升级版上市了!

图灵教育

可复现、开放科研、跨学科合作:数据驱动下的科研趋势及应用方案

ModelWhale

数据分析 代码复现 开放生态 协同软件 科研成果

ICDAR比赛技术分享

之家技术

算法 人工只能 竞赛 ICDAR

开源一夏 | 打工人的第25天-曾经的考研人

Amazing_eve

#开源

视频人脸识别和图片人脸识别的关系

夏夜许游

人脸识别 视频人脸识别

高效的组织信息共享知识库是一种宝贵的资源

Geek_da0866

面试不再被吊打!这才是Redis分布式锁的七种方案的正确打开方式

程序员小毕

Java 架构 面试 分布式 分布式锁

「一篇终结JVM」:Java面试必问十个JVM核心知识点梳理

Java全栈架构师

Java 程序员 面试 程序人生 JVM

国内首发可视化智能调优平台,小龙带你玩转KeenTune UI

OpenAnolis小助手

开源 龙蜥社区 sig KeenTune 一键式性能调优

一次做数据报表的踩坑经历,让我领略了数据同步增量和全量的区别

百思不得小赵

数据同步 增量同步 全量同步 签约计划第三季 8月月更

ModelWhale 云端运行 WRF 中尺度数值气象模式,随时随地即开即用的一体化工作流

ModelWhale

数据科学 气象 全流程一体化 WRF 大气科学

技术干货|如何将 Pulsar 数据快速且无缝接入 Apache Doris

SelectDB

数据库 Doris pulsar 数据导入 kafaka

为何微博又双叒叕崩溃了?

华仔

带你了解什么是 Web3.0

liuzhen007

Web 3.0

中小微企业如何简单便捷、低成本实现数字化?360视觉云有妙招

ToB行业头条

基于Java的插件化集成项目实践

阿提说说

微服务 插件化

基于DMS的数仓智能运维服务,知多少?

华为云开发者联盟

数据库 后端 监控 智能运维

浅谈Service Mesh对业务系统的价值

HelloGeek

微服务 云原生 server mesh 微服务治理

秀到飞起!Alibaba全新出品JDK源码学习指南(终极版)限时开源

Java全栈架构师

程序员 程序人生 jdk源码 Java 面试 架构师

生物统计师与临床医生协同研究使用的低代码洞察平台丨数据科学 x 临床医学

ModelWhale

团队协作 Jupyter Notebook 数据科学 低代码开发 临床医学

出海季,互联网出海锦囊之本地化

融云 RongCloud

元宇宙 IT业界

不可忽略!户外LED显示屏的特点及优势

Dylan

LED显示屏 户外LED显示屏 led显示屏厂家

利用java实现视频人像分割及视频背景替换

夏夜许游

Java 图像分割 视频人像分割 背景替换

开源一夏 | 阿里云物联网平台之极速体验

六月的雨在InfoQ

阿里云 开源 物联网 8月月更

开源一夏 |我在滴滴做开源

石臻臻的杂货铺

开源 8月月更

详谈RDMA技术原理和三种实现方式

C++后台开发

网络协议 C/C++后台开发 C/C++开发 RDMA技术 以太网

融云「音视频架构实践」技术专场【内含完整PPT】

融云 RongCloud

音视频技术

View的异步Inflate+全局缓存:加速你的页面_文化 & 方法_阿里巴巴文娱技术_InfoQ精选文章