写点什么

Android 布局优化技巧大盘点,最新 BAT 大厂面试者整理的 Android 面试题目

用户头像
Android架构
关注
发布于: 刚刚


measure、layout、draw 流程:



注:图片来源于工匠若水


https://blog.csdn.net/yanbober

/? ?优化工具? ?/

首先简单介绍下绘制优化相关的工具,这里 systrace 和 traceView 依然好使,按绘制流程阶段发现绘制耗时函数。这部分同卡顿篇原理一致就不赘述了。


2.1 Lint


静态代码检测工具,通过对代码进行静态分析,可以帮助开发者发现代码质量问题和提出一些改进建议。AS 中目前大概有 200 个左右的 lint 检查,当然有特殊需求的可以自定义:【我的 Android 进阶之旅】Android 自定义 Lint 实践


https://blog.csdn.net/ouyang_peng/article/details/80374867


这里简单看下布局相关的两个检查项:



点击 Analyze 的 Inspect Code 触发 Lint 检测



2.2 show GPU overdraw & GPU rendering


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


Settings/开发者选项/调试 GPU 过度绘制



Settings/开发者选项/HWUI 呈现模式分析


1)在屏幕上显示为条形图:



2)adb shell dumpsys gfxinfo


https://developer.android.com/training/testing/performance


2.3 Layout Inspector


https://www.jianshu.com/p/1b64024f2d08


AS:Tools > Android > Layout Inspector 选择对应进程



左侧看视图层级结构,右侧看具体属性和赋值内容。

/? ?监控? ?/

3.1 布局整体耗时监控:


可以使用 Aspect


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


J 做面向 aop 的非侵入性的监控。


工程主 gradle:


classpath?'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0’


项目 gradle:


apply?plugin:?'android-aspectjx’implementation?'org.aspectj:aspectjrt:1.8.+’


针对 Activity.setContentView 监控简单示例:


@Aspectpublic?class?PerformanceAop?{public?static?final?String?TAG?=?"aop";@Around("execution(*?android.app.Activity.setContentView(..))")public?void?getSetContentViewTime(ProceedingJoinPoint?joinPoint)?{Signature?signature?=?joinPoint.getSignature();String?name?=?signature.toShortString();long?time?=?System.currentTimeMillis();try?{joinPoint.proceed();}?catch?(Throwable?throwable)?{throwable.printStackTrace();}Log.i(TAG,?name?+?"?cost?"?+?(System.currentTimeMillis()?-?time));}}


3.2 单个视图创建耗时监控:


Factory2、Factory 本质上他俩就是创建 View 的一个 hook,可以通过这个回调来监控单个 View 创建耗时情况。


注:Factory2 继承自 Factory,Factory2 比 Factory 的 onCreateView 方法多一个 parent 的参数,即当前创建 View 的父 View。


简单示例:


LayoutInflaterCompat.setFactory2(getLayoutInflater(),?new?LayoutInflater.Factory2()?{@Nullable@Overridepublic?View?onCreateView(@Nullable?View?parent,?@NonNull?String?name,?@NonNull?Context?context,?@NonNull?AttributeSet?attrs)?{//1.配合 getDelegate().createView 来做高版本控件的兼容适配。//2.单个 View 创建耗时统计。long?time?=?System.currentTimeMillis();View?view?=?getDelegate().createView(parent,?name,?context,?attrs);Log.i("TAG",?name?+?"??cost:?"?+?(System.currentTimeMillis()?-?time));return?view;}


@Nullable@Overridepublic?View?onCreateView(@NonNull?String?name,?@NonNull?Context?context,?@NonNull?AttributeSet?attrs)?{return?null;}});


这里有一点要注意:setFactory2 必须在 super.onCreate(savedInstanceState)之前,不然会报如下错误:


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?LayoutInflaterat?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:?132020-03-11?16:43:07.569?17078-17078/com.stan.topnews?I/perf:?ViewStub??cost:?02020-03-11?16:43:07.634?17078-17078/com.stan.topnews?I/perf:?TextView??cost:?162020-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()?{@Overridepublic?void?onDraw()?{Choreographer.getInstance().postFrameCallback(new?Choreographer.FrameCallback()?{@Overridepublic?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)

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android布局优化技巧大盘点,最新BAT大厂面试者整理的Android面试题目