写点什么

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:355885

评论 2 条评论

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

核心竞争力,到底是什么?

老张

竞争力 职场成长 核心竞争力

什么是数据资产管理?数据资产管理包括了哪些内容?

优秀

数据资产管理

CCIG 2024:合合信息文档解析技术突破与应用前景

海拥(haiyong.site)

文档识别

Scroll 生态明星项目Pencils Protocol,发展潜力巨大

股市老人

反VC情绪:加密市场需要新的分布式代币发行方式

BlockChain先知

一文解析Scroll 生态明星项目Pencils Protocol,严重被低估

西柚子

反VC情绪:加密市场需要新的分布式代币发行方式

股市老人

怎么把Python脚本打包成可执行程序?

我再BUG界嘎嘎乱杀

Python 编程 后端 开发语言

GPT-4o API 申请开发部署应用:一篇全面的指南

蓉蓉

openai gpt4o

Moonchain 与穿戴设备 Blueberry Ring 整合,基于自身 DePIN 生态为 IoT 领域深度赋能

股市老人

ChatGPT-4o有什么特别之处?

石臻臻的杂货铺

AI ChatGPT ChatGPT4

Scroll 上的明星项目Pencils Protocol ,缘何被严重低估?

石头财经

Scroll 生态明星项目Pencils Protocol,发展潜力巨大

BlockChain先知

反VC情绪:加密市场需要新的分布式代币发行方式

大瞿科技

Maven 中的 classifier 属性用过没?

江南一点雨

Java maven

利用依赖结构矩阵管理架构债务

俞凡

架构

Scroll 上的明星项目Pencils Protocol ,缘何被严重低估?

股市老人

30天拿下Rust之输入输出

希望睿智

rust语言 输入输出

30天拿下Rust之命令行参数

希望睿智

命令行 rust语言 命令行参数解析

如何在 JS 中快速读取文件

南城FE

JavaScript 前端 nodejs

Apache IoTDB进行IoT相关开发实践

芯动大师

应用 IoTDB Apache IoTDB

批量采集抖音商品详情数据:推荐你使用API(通过商品id取商品详情商品主图sku属性)

tbapi

抖音商品详情数据接口 抖音API

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