Android布局优化技巧大盘点,电子版已问世

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.stan.topnews/com.stan.topnews.app.MainActivity}: java.lang.IllegalStateException: A factory has already been set on this LayoutInflater
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3314)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3453)

打印结果:

2020-03-11 16:43:07.389 17078-17078/com.stan.topnews I/Perf: Connecting to perf service.
2020-03-11 16:43:07.567 17078-17078/com.stan.topnews I/perf: LinearLayout  cost: 13
2020-03-11 16:43:07.569 17078-17078/com.stan.topnews I/perf: ViewStub  cost: 0
2020-03-11 16:43:07.634 17078-17078/com.stan.topnews I/perf: TextView  cost: 16
2020-03-11 16:43:07.637 17078-17078/com.stan.topnews I/perf: TextView  cost: 3

3.3 布局绘制监控

这里用到的还是FPS,就监控一个doFrame。

简单示例:

private long mStartFrameTime = 0;
private int mFrameCount = 0;
/**
* 单次计算FPS使用160毫秒
/
private static final long MONITOR_INTERVAL = 160L;
private static final long MONITOR_INTERVAL_NANOS = MONITOR_INTERVAL * 1000L * 1000L;
/
*
* 设置计算fps的单位时间间隔1000ms,即fps/s
*/
private static final long MAX_INTERVAL = 1000L;
private void getFPS() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
return;
}

getWindow().getDecorView().getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
@Override
public void onDraw() {
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
if (mStartFrameTime == 0) {
mStartFrameTime = frameTimeNanos;
}
long interval = frameTimeNanos - mStartFrameTime;
if (interval > MONITOR_INTERVAL_NANOS) {
double fps = (((double) (mFrameCount * 1000L * 1000L)) / interval) * MAX_INTERVAL;
Log.i(TAG, “fps:” + fps);
mFrameCount = 0;
mStartFrameTime = 0;
} else {
++mFrameCount;
}
}
});
}
});
}

FPS相关成熟三方库:

matrix 微信的卡顿检测方案,采用的ASM插桩的方式,支持fps和堆栈获取的定位,但是需要自己根据asm插桩的方法id来自己分析堆栈,定位精确度高,性能消耗小,比较可惜的是目前没有界面展示,对代码有一定的侵入性。如果线上使用可以考虑。

fpsviewer 利用Choreographer.FrameCallback来监控卡顿和Fps的计算,异步线程进行周期采样,当前的帧耗时超过自定义的阈值时,将帧进行分析保存,不影响正常流程的进行,待需要的时候进行展示,定位。

/  布局加载优化  /

前面简单了解了布局加载流程,

性能瓶颈在于LayoutInflater.inflater过程,主要包括如下两点:

  • xmlPullParser IO操作,布局越复杂,IO耗时越长。

  • createView 反射,View越多,反射调用次数越多,耗时越长,但是这必须达到一定量级才会有明显影响。Java反射到底慢在哪?

那么很容易想到两个解决办法:要么把IO和反射交由子线程来处理,要么通过动态加载视图把IO和反射规避掉。那么市面上有没有相关的成熟方案呢?当然是有的,下面来简单看一看:

AsyncLayoutInflater

https://developer.android.com/reference/android/support/v4/view/AsyncLayoutInflater

AsyncLayoutInflater是google提供的方案,让LayoutInflater.inflater过程通过子线程来做:

new AsyncLayoutInflater(AsyncLayoutActivity.this)
.inflate(R.layout.async_layout, null, new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
public void onInflateFinished(View view, int resid, ViewGroup parent) {
setContentView(view);
}
});

实现也很简单:handle+thread+queue+inflater。可以理解为具有loop能力的子线程来实现的耗时部分异步处理。

这里有两点局限性:

  • 不能设置LayoutInflater.Factory/Factory2

  • 线程安全问题

详细源码分析和自定义AsyncLayoutInflater解决局限性问题可以参考如下文章,我就不重复造轮子了:

Android AsyncLayoutInflater 源码解析

https://www.jianshu.com/p/a3a3bd314c45

Android AsyncLayoutInflater 限制及改进

https://www.jianshu.com/p/f0c0eda06ae4

X2C

https://github.com/iReaderAndroid/X2C/blob/master/README_CN.md

动态加载视图,这样能避免IO和反射,但是这样缺点是可读性差、可维护性差,因此掌阅团队开发的X2C做了鱼和熊掌都兼得的方案:X2C,它原理是采用APT(Annotation Processor Tool)+ JavaPoet技术来完成编译期间视图xml布局生成java代码,这样布局依然是用xml来写,编译期X2C会将xml转化为动态加载视图的java代码。

这里个人理解可能存在的局限性:

  • 失去系统兼容AppCompat

  • 是不是能全面支持所有布局属性及自定义属性

  • 如果视图全部用X2C来处理,会造成代码冗余。

/  布局绘制优化  /

这部分是由ViewRootImpl触发的performTraversals,它主要包含:measure(确定ViewGroup以及View的大小) layout(ViewGroup决定View的摆放位置) draw(绘制视图)三个部分。另外,绘制好的DisplayListOp tree最终需要经过OpenGL命令转换交由GPU渲染,如果同一个像素点被多次重复绘制,势必也是造成浪费以及GPU任务变重。

因此布局绘制最终优化方向就是如下两个:

5.1 优化布局层级及其复杂度

measure、layout、draw这三个过程都包含的自顶向下的view tree遍历耗时,它是由视图层级太深会造成耗时,另外也要避免类似RealtiveLayout嵌套造成的多次触发measure、layout的问题。最后onDraw在频繁刷新时可能多次被触发,因此onDraw不能做耗时操作,同时不能有内存抖动隐患等。

优化思路:

  • 减少View树层级

  • 布局尽量宽而浅,避免窄而深

  • ConstraintLayout 实现几乎完全扁平化布局,同时具备RelativeLayout和LinearLayout特性,在构建复杂布局时性能更高。

  • 不嵌套使用RelativeLayout

  • 不在嵌套LinearLayout中使用weight

  • merge标签使用:减少一个根ViewGroup层级

  • ViewStub 延迟化加载标签,当布局整体被inflater,ViewStub也会被解析但是其内存占用非常低,它在使用前是作为占位符存在,对ViewStub的inflater操作只能进行一次,也就是只能被替换1次。

5.2 避免过度绘制

一个像素最好只被绘制一次。

优化思路:

  • 去掉多余的background,减少复杂shape的使用

  • 避免层级叠加

  • 自定义View使用clipRect屏蔽被遮盖View绘制

5.3 视图与数据绑定耗时

由于网络请求或者复杂数据处理逻辑耗时导致与视图绑定不及时。这里可以从优化数据处理的维度来解决。

/  Litho介绍  /

Litho

https://fblitho.com/docs/intro

是 FaceBook 2017年上半年开源的声明式UI渲染框架。

主要针对RecyclerView复杂滑动列表做了以下几点优化:

视图的细粒度复用,可以减少一定程度的内存占用。

异步计算布局,把测量和布局放到异步线程进行。

扁平化视图,把复杂的布局拍成极致的扁平效果,优化复杂列表滑动时由布局计算导致的卡顿问题。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

img
img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
img

结尾

最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

知道以后的路怎么走了,理论看多了总要实践的。

[外链图片转存中…(img-6WZ7nMz8-1711549552865)]

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

[外链图片转存中…(img-YgeQJaC1-1711549552866)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 18
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值