很多开发者在工作中一直和UI打交道,所以认为UI非常的简单!
事实上对于90%的开发者来说,不知道UI的本质原理。
虽然在开发中,我们在接到产品的UI需求之后,可以走捷径照抄大型APP代码,但是copy来的代码一旦出了问题,也只是百度或者靠猜的方案去解决。
真正高级的工程师也会使用别人的代码,但是他们深入理解了高级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优化主要包括布局优化以及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对象的。
耗时的地方:
下面介绍一些常用的UI优化方式~
使用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。
通过子线程创建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.");
}
}
复用View这个应该比较常见了,像RecyclerView的四级缓存,目的就是通过复用View减少创建View的时间。
我们可以在onDestroy方法将View的状态清除,然后放入缓存。在onCreate的时候去命中缓存,设置状态。
正常情况下measure、layout、draw都是在主线程执行的,最终绘制操作是在draw方法,而measure、layout只是做一些数据准备,完全可以放到子线程去做。
Litho 的原理就是将measure、layout 放到子线程: github.com/facebook/li…
优点:
缺点:
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、程序性能优化
3、开发效率优化
4、项目实战
手机扫一扫
移动阅读更方便
你可能感兴趣的文章