【AICon】探索八个行业创新案例,教你在教育、金融、医疗、法律等领域实践大模型技术! >>> 了解详情
写点什么

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

评论 2 条评论

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

面向对象的设计模式

WW

架构师训练营第三周作业和小记

tuuezzy

架构师 极客大学架构师训练营

面向对象设计模式课程小结

梅子黄时雨

极客大学架构师训练营

架构师训练营 第三周 命题作业

RZC

架构师训练营 第三周 学习总结

RZC

2020互联网公司端午节礼盒合集!你最中意哪一款?

Java小咖秀

互联网人 端午节

可读代码编写炸鸡二(下篇) - 命名的歧义

多选参数

代码 代码优化 代码组织 代码规范

rodert单排学习redis进阶【白银一】

JavaPub

Java nosql redis

组合设计模式编码&手写单例模式

吴建中

极客大学架构师训练营

组合模式应用

yupi

区块链改变数字营销与广告市场

CECBC

区块链技术 广告业 精准投放 去中介 公开透明

数字货币监管当体现“中国之治”

CECBC

数字货币 CECBC 区块链技术 技术标准 准入和监管

架构师是怎样炼成的-3-2-设计模式

闷骚程序员

蟒周刊/426: DjangoCon US 2020 取消了

ZoomQuiet大妈

Python 大妈 蟒营® Weekly 蟒周刊

Zookeeper集群模式启动

tunsuy

zookeeper 源码分析 socket 分布式集群

第三周总结

晨光

良心推荐 | LeetCode(力扣),算法、数据结构的学习良伴

YoungZY

算法

手写单例模式

yupi

第三周作业

晨光

让你眼前一亮的 10 大 TS 项目

阿宝哥

Java typescript 开源 大前端 Web

可读代码编写炸鸡二(上篇) - 命名的长度

多选参数

代码 代码组织 代码规范

新手村:最适合新手的 Redis 基础

多选参数

数据库 redis redis6.0.0

产品失败了,产品经理要不要承担责任?

涛哥 数字产品和业务架构

产品经理

Zookeeper通信协议详解

tunsuy

zookeeper TCP/IP 通信协议

windows使用docker运行mysql等工具(二)安装运行mysql

Java旅途

MySQL Docker

极客大学架构师训练营 框架开发 第三次作业

John(易筋)

极客时间 设计模式 极客大学 极客大学架构师训练营 框架开发

一个汉字占几个字节你真的记住了吗?

Java旅途

[架构师训练营] Week01 -学习总结

谭方敏

Zookeeper的数据剖析

tunsuy

zookeeper 日志分析 事务 快照 数据恢复

小师妹学JVM之:java的字节码byte code简介

程序那些事

Java JVM bytecode 字节码 签约计划第二季

windows使用docker运行mysql等工具(一)windows安装docker

Java旅途

MySQL Docker

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