前置知识
- 有Android开发基础
- 了解Android运行原理
- 了解何为性能优化,以及清楚性能优化的思路
- 已阅读 浅谈Android的流畅性优化——归因分析
前言
学习了前文的归因分析,本篇文章就带大家以实际的案例进行浅要的分析和给出具体的优化策略,具体的案例来自于网络,如有错误,还请指出。
前文回顾
前文中,我们讲到了流畅性优化中一个布局从 xml 到成为一个 View 对象有哪些耗时的问题。我们可以回顾一下,如下图,我们可以把上文中 View 对象的创建以及其渲染等耗时问题分为以下的三点,包括 UI 构建、数据绑定 以及 View 的显示。对于上述这三点,我们可以逐个进行优化。
UI 构建
UI 的构建问题中,我们有遇到诸如:载入 XML 的 IO 问题、class 的反射问题、创建 View 的问题、Asset 大锁问题。这些耗时都是由于在主线程中发生,所以导致其影响我们的流畅性。对此,Google 有提出一种异步加载布局的方案:AsyncLayoutInflater
- AsyncLayoutInflater 解决方案
这个方案可以对布局进行异步的加载,其可以应用在提前知晓布局的显示,以及对布局显示的速度没有要求的情况下进行使用。例如弹窗布局放置于异步操作就没有问题,它可以提前在异步准备好操作。但是这类解决方案的劣势即是无法即时响应,且我们没有那么多情况下是可以提前知晓、准备好布局的展示。
// 异步加载 xml, 在 Activity.onCreate(xxx) 里面 AsyncLayoutInflater(this).inflate(R.layout.activity_try_everything, null, object : AsyncLayoutInflater.OnInflateFinishedListener { override fun onInflateFinished(view: View, p1: Int, p2: ViewGroup?) { setContentView(view) } }) 复制代码
- 所以说,这个 Google 的方案是不能适用于我们大部分的使用场景的,下面我们可以看一下抖音的优化方案。
- 抖音的解决方案
- 使用 X2C 解决 xml 的性能问题。
直接把 xml 在编译阶段转化为 java 代码,这样就无需访问 IO 做反射生成布局了。 - 高优先级的启动预加载方案。
在Application#onCreate()
阶段对布局进行预加载,到了setContentView()
时候就直接使用预加载好的布局即可。 - 随时预加载,不与具体的逻辑绑定,生命周期存货,可自定义清理生命周期。
使用并发和字节码方式对其动态的生成
数据绑定
由于当下的 App 会有大量的网络请求,所以对网络请求数据的优化也是能带来很大收益的。
- Gson 解析优化
对于 Gson 的解析,我们每次使用已创建好的对象或者每次创建新的对象解析所带来的时间差异也是巨大的。
//with Cache Gson gson = new Gson(); for(...){ gson.from(...); } //without Cache for(...){ new Gson().from(...); } 复制代码
- 对于上述两种情况,其耗时如下,可见使用 Cache 带来的收益是很大的。
- 数据协议优化:json -> protobuf
protobuf 对于数据无论是压缩或者是不压缩的时候,由于其体积小使得传输时间远比 json 要小得多,其带来的收益是很大的。
View的渲染问题
对于 View 的渲染优化,我们可以有以下点
- 移除不必要的背景图
- 修改不合理的布局
- 写高效的合理布局
- 移除默认的 Window 布局
- 绘制层级的优化
对于上述的这些渲染中出现的耗时点,我们可以使用将绘制、渲染放置于异步操作,交互放置于主线程中。对于这个问题,不同的公司有不同的解决方案。
- SurfaceView
采用独立的线程进行绘制和渲染,自行控制生命周期。SurfaceView详解 - jetpack Compose
这是另一套 View 逻辑,用于替代现有的View。其使用组合优于继承的思想,重构出一套解耦的 UI 框架,这使得 View 更容易在异步进行绘制。
- FaceBookLitho:在Android上构建高效UI的声明性框架。可以应用于复杂 UI 下进行高性能渲染。
- 扁平化:其使用异步线程提前执行 measure 和 draw 方法,通过将布局系统与传统的Android View系统分离,Litho可以摆脱Android强加的UI线程限制。且 Litho 使用 Yoga 进行布局,并自动减少 UI 中包含的 ViewGroup 数(扁平化思想)。除了Litho的文本优化之外,这还允许更小的视图层次结构,并提高了内存和滚动性能。
- 组件化:Litho 使用 Drawable 作为 Node 的绘制单元,实现了布局细粒度复用(使用Litho,每个UI项(如文本、图像或视频)都会单独回收。一旦一个项目离开屏幕,它就可以在UI中的任何地方重用,并与其他项目拼凑在一起,以创建新的UI元素。)和异步计算布局的能力。
- 声明式 UI :需要重学,重构这套 UI 的编写,这也是其中的缺点。
- 抖音
- SurfaceView 和 TexureView 一起使用。前者是可以有单独层级,后者就是和 DecorView 一个层级。