前言

很多开发者在工作中一直和UI打交道,所以认为UI非常的简单!

事实上对于90%的开发者来说,不知道UI的本质原理。

虽然在开发中,我们在接到产品的UI需求之后,可以走捷径照抄大型APP代码,但是copy来的代码一旦出了问题,也只是百度或者靠猜的方案去解决。

真正高级的工程师也会使用别人的代码,但是他们深入理解了高级UI的原理及性能优化方法,就能避免卡顿的情况。

相信大家多多少少看过一些高级UI原理的文章,但是一到用的时候就不知道了,本文就给大家讲清楚!

从生命周期分析UI原理

  • Activity的attach 方法里创建PhoneWindow。

  • onCreate方法里的 setContentView 会调用PhoneWindow的setContentView方法,创建DecorView并且把xml布局解析然后添加到DecorView中。

  • 在onResume方法执行后,会创建ViewRootImpl,它是最顶级的View,是DecorView的parent,创建之后会调用setView方法。

  • ViewRootImpl 的 setView方法,会将PhoneWindow添加到WMS中,通过 Session作为媒介。setView方法里面会调用requestLayout,发起绘制请求。

  • requestLayout 一旦发起,最终会调用 performTraversals 方法,里面将会调用View的三个measure、layout、draw方法,其中View的draw 方法需要一个传一个Canvas参数。

  • 最后通过relayoutWindow 方法将Surface跟当前Window绑定,通过Surface的lockCanvas方法获取Surface的的Canvas,然后View的绘制就通过这个Canvas,最后通过Surface的unlockCanvasAndPost 方法提交绘制的数据,最终将绘制的数据交给SurfaceFlinger去提交给屏幕显示。

UI优化

UI优化主要包括布局优化以及view的绘制优化。先说下UI的优化到底是什么?

有些时候我们打开某个软件,会出现卡顿的情况。这就是UI的问题。那么我们想一下,什么情况会导致卡顿呢?一般是如下几种情况:

  • 人为在UI线程中做轻微耗时操作,导致UI线程卡顿;

  • 布局Layout过于复杂,无法在16ms内完成渲染;

  • 同一时间动画执行的次数过多,导致CPU或GPU负载过重;

  • View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重;

  • View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染;

  • 内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作;

  • 冗余资源及逻辑等导致加载和执行缓慢;

  • 臭名昭著的ANR;

可以看见,上面这些导致卡顿的原因都是我们平时开发中非常常见的。

有些人可能会觉得自己的应用用着还蛮OK的,其实那是因为你没进行一些瞬时测试和压力测试,一旦在这种环境下运行你的App你就会发现很多性能问题。

下面就分享给大家几种常见的UI 优化方式。

所谓UI优化,就是拆解渲染过程的耗时,找到瓶颈的地方,加以优化。

前面分析了UI原理,Activity、Window、DecorView、ViewRootImpl之间的关系,以及XML布局文件是如何解析成View对象的。

耗时的地方:

  • View的创建在主线程,包括measure、layout、draw,界面复杂的时候,这一部分可能会很耗时。
  • 解析XML,反射创建VIew对象,这一过程的耗时。

下面介绍一些常用的UI优化方式~

3.1 常规方式

  1. 减少UI层级、使用merge、Viewstub标签优化
  2. 优化layout开销、RelativeLayout和带有weight的Linearlayout会测量多次,可以尝试使用ConstraintLayout 来代替。
  3. 背景优化,分析DecorView创建的时候,发现DecorView会设置一个默认背景,可以统一将DecorView背景设置为通用背景,其它父控件就无需设置背景,避免重复绘制。

3.2 xml转换成代码

使用xml编写布局,很方便,但是最终要通过LayoutInflater的inflate方法,将xml解析出来并递归+反射去创建View对象,布局比较复杂的时候,这一部分会非常耗时。

使用代码创建可以减少xml递归解析和反射创建View的这部分耗时。 当然,如果将xml都换成代码来写,开发效率将不忍直视,并且代码可读性也是个问题。

掌阅开源的一个库,编译期自动将xml转换成java代码,X2C

它的原理是采用APT(Annotation Processor Tool)+ JavaPoet技术来完成编译期间【注解】-【解注解】->【翻译xml】->【生成java】整个流程的操作

即在编译生成APK期间,将需要翻译的layout翻译生成对应的java文件,这样对于开发人员来说写布局还是写原来的xml,但对于程序来说,运行时加载的是对应的java文件。

侵入性极低,去除注解则回退到原生的运行时解析方式。当然,有些情况是不支持转换的,比如merge标签,编译期没法确定它的parent。

3.3 异步创建View

通过子线程创建View,减少主线程耗时。

private void threadNewView() {
new Thread(){
@Override
public void run() { mSplashView = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_splash,null);
}
}.start();
}

当然,这种方式需要处理同步问题,并且没有从源头上解决创建View耗时,只是将这部分耗时放到线程去做。UI更新的操作还是要切换到主线程,不然会触发ViewRootImpl的checkThread检测。

void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}

3.4 复用View

复用View这个应该比较常见了,像RecyclerView的四级缓存,目的就是通过复用View减少创建View的时间。

我们可以在onDestroy方法将View的状态清除,然后放入缓存。在onCreate的时候去命中缓存,设置状态。

3.5 异步布局: Litho

正常情况下measure、layout、draw都是在主线程执行的,最终绘制操作是在draw方法,而measure、layout只是做一些数据准备,完全可以放到子线程去做。

Litho 的原理就是将measure、layout 放到子线程: github.com/facebook/li…

优点:

  1. 将measure、layout、放到子线程去做,减少主线程耗时。
  2. 使用自己的布局引擎,减少View层级,界面扁平化。
  3. 优化RecyclerView,提高缓存命中率。

缺点:

  1. 无法在AS中预览。
  2. 使用自己的布局引擎,有一点的使用成本。

3.6 Flutter:自绘引擎

Flutter 是一个跨平台UI框架,内部集成了Skia图像库,自己接管了图像绘制流程,性能直逼原生,是时候制定计划学习一波了~


学习的过程中善于总结才能快速提升个人的水平,这里我也总结了一份《Android性能优化全方面解析》,1586页,5个章节,95个小点,不仅仅有详细的底层原理的解析,还有大厂性能优化探索与实践!

第一章 性能优化心得与经验

  • 移动端性能监控方案Hertz

  • Android性能优化之虚拟机调优

  • Android UI 性能优化

  • 美团外卖Android Lint代码检查实践

  • 使用Android Studio和MAT进行内存泄漏分析

    ......

第二章 响应速度

  • Android App 启动优化全记录

  • Android 中如何计算 App 的启动时间?

  • 应用启动时间

  • 支付宝 App 构建优化解析

  • Redex 初探与 Interdex:Andorid 冷启动优化

    ......

第三章 流畅度

  • Android 中的卡顿丢帧原因概述

  • Android 无障碍服务导致的整机卡顿案例分析

  • 显示性能指标

  • 渲染速度缓慢

  • Android 流畅度检测原理简析

    ......

第四章 内存

  • Android 中低内存对性能的影响

  • Android OOM案例分析

  • Android 代码内存优化建议

  • Android匿名共享内存(Ashmem)原理

  • Linux 查看进程消耗内存情况总结

    ......

第五章 图形栈

  • Android 中的 Hardware Layer 详解

  • Android硬件加速原理与实现简介

  • Android图形系统概述

  • Choreographer原理

  • SurfaceFlinger启动篇

  • Android应用程序UI硬件加速渲染技术

    ......

还有一份《360°全方面性能调优》一共有721页,四个章节,25个小点。

1、设计思想与代码质量优化

2、程序性能优化

  • 启动速度与执行效率优化
  • 布局检测与优化
  • 内存优化
  • 耗电优化
  • 网络传输与数据储存优化
  • APK大小优化

3、开发效率优化

  • 分布式版本控制系统Git
  • 自动化构建系统Gradle

4、项目实战

  • 启动速度
  • 流畅度
  • 抖音在APK包大小资源优化的实践
  • 优酷响应式布局技术全解析
  • 网络优化
  • 手机淘宝双十一性能优化项目揭秘
  • 高德APP全链路源码依赖分析
  • 彻底干掉OOM的实战经验分享
  • 微信Android终端内存优化实践

需要的朋友只需要 点赞支持一下 后,然后【点击这里免费获取

90%的开发者都不知道的UI本质原理和优化方式的更多相关文章

  1. 90% 前端开发者都不知道的 JavaScript 实用小技巧

    面试神器之数组去重 const a = [...new Set([1, 2, 3, 3])] >> [1, 2, 3] 操作数组担心 falsy 值? const res = myArra ...

  2. 90%的人都不知道的Node.js 依赖关系管理(上)

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文参考:https://dzone.com/articles/nodejs-dependency-mana ...

  3. 90%的人都不知道的Node.js 依赖关系管理(下)

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文参考:https://dzone.com/articles/node-dependency-manage ...

  4. 关于 junit4 90% 的人都不知道的特性,详解 junitperf 的实现原理

    前言 上一节介绍了 https://github.com/houbb/junitperf 的入门使用. 这一节我们从源码的角度,剖析一下其实现方式. 性能测试该怎么做? Junit Rules jun ...

  5. 震惊!90%的程序员不知道的Java知识!

    震惊!90%的程序员不知道的Java知识! 初学Java的时候都会接触的代码 public static void main(String[] args){ ... } 当时就像背公式一样把这行代码给 ...

  6. [转] 你是as3老鸟吗?但是有些你可能目前都不知道的东西

    你是as3老鸟吗?如果以下内容对你有莫大的帮助,请顶下! 一:加载swf库中的图片 new 的过程就是图片解压缩的过程.处于 Class 状态时,图片占用的内存和 SWF 文件中这个图片占用的磁盘空间 ...

  7. 很多人都不知道的监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法

    版权声明:本文为博主原创文章,未经博主允许不得转载. 在实际的应用中,我们常常需要实现在移动app和浏览器中点击返回.后退.上一页等按钮实现自己的关闭页面.调整到指定页面或执行一些其它操作的 需求,那 ...

  8. 一文读懂架构师都不知道的isinstance检查机制

    起步 通过内建方法 isinstance(object, classinfo) 可以判断一个对象是否是某个类的实例.但你是否想过关于鸭子协议的对象是如何进行判断的呢? 比如 list 类的父类是继 o ...

  9. 99% 的人都不知道的 Kubernetes 网络疑难杂症排查方法

    原文链接:Kubernetes 网络疑难杂症排查分享 大家好,我是 roc,来自腾讯云容器服务 (TKE) 团队,经常帮助用户解决各种 K8S 的疑难杂症,积累了比较丰富的经验,本文分享几个比较复杂的 ...

随机推荐

  1. CentOS7日志管理工具 journalctl

    1.简介 日志管理工具journalctl是centos7上专有的日志管理工具,该工具是从message这个文件里读取信息. Systemd统一管理所有Unit的启动日志.带来的好处就是,可以只用jo ...

  2. Kubernetes使用节点亲缘性将POD调度到特定节点上

    节点污点可以用来让pod远离特定的节点,尽量在不修改已有pod信息的前提,通过在节点添加污点信息,来拒绝pod在某些节点上的部署. 而现在介绍一种叫做节点亲缘性,通过明确的在pod中添加的信息,来决定 ...

  3. 温故知新,CSharp遇见异步编程(Async/Await),聊聊异步编程最佳做法

    什么是异步编程(Async/Await) Async/Await本质上是通过编译器实现的语法糖,它让我们能够轻松的写出简洁.易懂.易维护的异步代码. Async/Await是C# 5引入的关键字,用以 ...

  4. LeetCode解题记录(贪心算法)(一)

    1. 前言 目前得到一本不错的算法书籍,页数不多,挺符合我的需要,于是正好借这个机会来好好的系统的刷一下算法题,一来呢,是可以给部分同学提供解题思路,和一些自己的思考,二来呢,我也可以在需要复习的时候 ...

  5. 暑假自学java第二天

    今天学习了一些java规则 一个java源文件的公开类只能有一个,而且必学和源文件名相同. 了解到java的标识符规范,这对以后的团队协作有很大作用. 标识符规则和c++还是很相似的 java中的字面 ...

  6. PHP大文件分片上传的实现方法

    一.前言 在网站开发中,经常会有上传文件的需求,有的文件size太大直接上传,经常会导致上传过程中耗时太久,大量占用带宽资源,因此有了分片上传. 分片上传主要是前端将一个较大的文件分成等分的几片,标识 ...

  7. XCTF easyGo

    拖入ida,发现符号表需要还原一下,载入一个还原符号表的脚本. go这个语言就有点恶心,字符串后面没有反斜杆零,ida识别出来,字符串就会挤在一堆,就很难看,看了某位师傅的wp,觉得这方法不错,就记录 ...

  8. DL基础补全计划(三)---模型选择、欠拟合、过拟合

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  9. RabbitMQ 安装与界面管理

    RabbitMQ 安装与界面管理 RabbitMQ概述 官网:https://www.rabbitmq.com/ RabbitMQ是部署最广泛的开源消息代理. RabbitMQ拥有成千上万的用户,是最 ...

  10. Pandas高级教程之:稀疏数据结构

    目录 简介 Spare data的例子 SparseArray SparseDtype Sparse的属性 Sparse的计算 SparseSeries 和 SparseDataFrame 简介 如果 ...