android布局优化,Android 优化
Litho粗读
Litho官网
公司主营业务:网站设计制作、网站设计、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联推出抚远免费做网站回馈大家。
Litho是Facebook推出的一套高效构建Android UI的声明式框架,主要目的是提升RecyclerView复杂列表的滑动性能和降低内存占用。
声明式组件
允许用户使用声明式的API(注解)来构建符合Flexbox规范的布局。
异步布局
支持组件挂载,异步线程执行measure和layout操作,UI线程完成绘制工作
扁平化布局
Litho使用Yoga来完成组件布局的测量和计算,而且支持将View转换成Drawable渲染,降低了View层级,实现了布局的扁平化
细粒度组件复用
支持Text、Image或者Video细粒度组件的复用,尤其在多ItemType的列表中有显著性能提升
Litho采用声明式的API来定义UI组件,组件通过一组不可变的属性来描述UI。这种组件化的思想灵感来源于React,实践过ReactNative或React都深有体会。不过声明式组件也有弊端,开发者不能直接预览布局编写,从而影响开发效率。
Android系统在绘制时为了防止页面错乱,页面所有View的测量(Measure)、布局(Layout)以及绘制(Draw)都是在UI线程中完成的。当页面UI非常复杂、视图层级较深时,难免Measure和Layout的时间会过长,从而导致页面渲染时候丢帧出现卡顿情况。
Litho鉴于此,提出了异步布局思想,最终实现了控件维度下异步线程完成measure和layout操作,在主线程完成draw操作。
首先,我们了解下 Android原生为什么不支持异步布局呢?
Litho的对策是怎样的呢 ?
View onMeasure时,在LayoutState中的clollectResults() 递归调用了nodeTree中的结点,最终调用yoga来计算结点的位置,目标就算是把所有的节点最终绘制到一个View中,降低View的层级。
View onLayout时,在MountState中,对所有的Component进行Mount调用,准备好需要绘制的具体内容,以待绘制调用。
同时Litho引入了缓存机制,可以提前在异步线程中进行measure和layout的操作,这样在绘制过程中只需要draw就可以了,理论提升效率35%(FaceBook宣传)
使用Litho布局,我们可以得到一个极致扁平的视图效果。它可以减少渲染时的递归调用,加快渲染速度。
下面是同一个视图在Android和Litho实现下的视图层级效果对比。可以看到,同样的样式,使用Litho实现的布局要比使用Android原生实现的布局更加扁平。
虽然平坦的视图结构对内存使用和绘图时间有重要的好处,但它们并非万能的灵丹妙药。Litho 有一个非常通用的系统来自动“ unflatten(堆叠起来) ” 已挂载组件的层次结构,当使用 Android View 里一些不可或缺的功能,如触摸事件的处理,无障碍功能,或限制失效等。例如,如果要在示例中启用单击图像或文本,则框架会自动将它们包装在视图中。
Litho中的所有组件都可以被回收,并在任何位置进行复用。这种细粒度的复用方式可以极大地提高内存使用率,尤其适用于多Item类型的复杂列表,内存优化非常明显。
RecyclerView 支持显示多样内容。为此,它根据视图的类型将视图保存在不同的缓冲池中。虽然这个概念在简单的情况下工作得很好,但是对于具有许多不同视图类型的 UI 来说,它可能会出现问题。在包含许多视图类型的场景中,在滚动事件之后进入窗口的视图更有可能是 RecyclerView 第一次显示的视图。如果发生这种情况,RecyclerView 必须分配一个新的视图。发生分配的 16ms 时间槽内 RecyclerView 也必须同时对即将显示的视图执行绑定、测量和布局操作
Litho 提供更具扩展性和高效率的回收系统,同时我们希望从 API 中消除视图类型的复杂性。 Litho 布局的表示与在屏幕上渲染布局的 View 和Drawable 完全脱节。这意味着,当我们需要在屏幕上放置一个新的 RecyclerView 视图时,我们已经知道该项目的内容以及它相对于其余 UI 的位置。 这使 Litho 完全摆脱 View 类型的概念。在 RecyclerView 滚动时,我们可以递增地使用文本或图像等构建块,而不是重新使用表示RecyclerView中项目的整个 View。 这对于传统的 Android 视图来说是不可能的,因为布局计算在完整的视图树上运行,并且当我们知道所有视图在一行中的位置时,所有视图都已经被实例化了。
InternalNode:由Component+(View)NodeInfo转化得到的表示布局信息的节点
DiffNode:InternalNode的轻量级表示形式,用于在两个布局树的计算之间缓存测量结果,如果不需要更新,那么直接复用DiffNode即可
MountSpec:对应的Component包含纯粹的View或者Drawable,比如TextSpec,ImageSpec
LayoutSpec:对应的Component类似于ViewGroup,内部组织管理了很多子Component,比如CardSpec,SpinnerSpec
什么条件复用DiffNode至InternalNode ?
参考
Litho的使用及原理剖析
LithoGitBook
android studio hierarchy viewer 怎么看布局是否需要优化
工具/原料
android 基础
91桌面
数据线
android手机
方法/步骤
在手机安装好91桌面哈,然后将页面滑到最左面的“快速搜索页”
hierarchyviewer.bat是sdk自带的工具,在tools文件夹下。双击即可打开
在此页面,加粗显示的就是当前进程,点击load view hierarchy就可以分层查看布局
左边的大图,为应用图层的树形结构。上面有控件名称和id等信息。然后下面三个圆点代表渲染的速度,绿色最快,红色最慢,其中从左到右依次表示的是测量大小,布局和绘制。再看右下角的那个数字,代表的是此节点在父节点中的索引。
整张图的右下角,表示的该应用的当前页面。在左边的树形图中点击某个节点,会在这里用红框标出响应的位置。左上角的图可以查看当前选中节点的具体布局数据,宽高什么的。
这里可以看出“快速搜索屏”的实现是,
draglayer--孩子---workspacelayer-----孩子0---workspace;
------孩子1---singleViewGroup;
singleViewGroup就是快速搜索屏的布局view
在网上找到一个图,觉得很好,介绍的精确完整,贴过来,给大家看看
Android 新控件之ConstraintLayout(约束布局)
ConstraintLayout (约束布局) 继承于ViewGroup 允许开发者以灵活的方式定位和调整小部件的大小
ConstraintLayout 可让开发者使用扁平视图层次结构(无嵌套视图组)创建复杂的大型布局。它与 RelativeLayout 相似,其中所有的视图均根据同级视图与父布局之间的关系进行布局,但其灵活性要高于 RelativeLayout ,并且更易于与 Android Studio 的布局编辑器配合使用。我理解为ConstraintLayout是一个更加灵活且减少嵌套的 RelativeLayout 的布局
ConstraintLayout作为支持库提供,开发者可以在从 API 级别 9 (Gingerbread) 开始的 Android 系统上使用。
相信在面对一些复杂的UI页面,咱们都是使用 RelativeLayout , LinearLayout 层层嵌套实现的.虽然能实现效果.但是层层嵌套层层解析加载View 无疑会耗费加载时间,耗费手机性能.这是时候ConstraintLayout(约束布局),就应运而生了,它出现的目的就是减少嵌套,优化层层嵌套状况带来的弊端
要在 ConstraintLayout 中定义某个视图的位置, 您必须为该视图添加至少一个水平约束条件和一个垂直约束条件 。每个约束条件均表示与其他视图、父布局或隐形引导线之间连接或对齐方式。每个约束条件均定义了视图在竖轴或者横轴上的位置;因此每个视图在每个轴上都必须至少有一个约束条件,但通常情况下会需要更多约束条件。
当您将视图拖放到布局编辑器中时,即使没有任何约束条件,它也会停留在您放置的位置。不过,这只是为了便于修改;当您在设备上运行布局时,如果视图没有任何约束条件,则会在位置 [0,0](左上角)处进行绘制。
在图 1 中,布局在编辑器中看起来很完美,但视图 C 上却没有垂直约束条件。在设备上绘制此布局时,虽然视图 C 与视图 A 的左右边缘水平对齐,但由于没有垂直约束条件,它会显示在屏幕顶部
请注意,约束中不能有循环依赖。
相对定位是在 ConstraintLayout 中创建布局的基本构建块之一。这些约束允许您相对于另一个小部件定位给定的小部件。您可以在水平和垂直轴上约束一个小部件:
如下图,这告诉系统我们希望按钮 B 的左侧被约束到按钮 A 的右侧。这样的位置约束意味着系统将尝试让两侧共享相同的位置。
这是可用约束的列表:
app:layout_constraintLeft(自身)_toLeftOf(相对于的控件)="相对的控件ID"
1.2 layout_constraintBaseline_toBaselineOf 基线对齐
如果设置了侧边距,它们将应用于相应的约束(如果存在)(图 ),将边距强制为目标端和源端之间的空间。通常的布局边距属性可用于此效果
2.1属性:
请注意,边距只能为正数或等于零,并且取Dimension.
2.2. 约束目标View.GONE的时候 的边距
3.1 居中定位,就是把定位控件的左边对应目标的左边 右边对应目标的右边,上边对应目标的上边
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
如上图,Button的左边位于父布局的左边,右边位于父布局的右边就做到了水平居中的效果
3.2 偏移 : 有时候居中展示还需要做出偏移效果
可以以一定角度和距离约束一个小部件中心相对于另一个小部件中心。这允许您将一个小部件定位在一个圆圈上
ConstraintLayout对标记为 的小部件进行了特定处理View.GONE。
GONE像往常一样,小部件不会被显示并且不是布局本身的一部分(即,如果标记为 ,它们的实际尺寸不会改变GONE)。
但就布局计算而言,GONE小部件仍然是其中的一部分,但有一个重要区别:
注意:
使用的边距将是 B 在连接到 A 时定义的边距(参见图 7 示例)。在某些情况下,这可能不是您想要的边距(例如,A 到其容器的一侧有 100dp 的边距,B 到 A 的边距只有 16dp,将 A 标记为已消失,B 到容器的边距为 16dp)。出于这个原因,您可以指定在连接到被标记为已消失的小部件时使用的备用边距值(请参阅 上面有关已消失的边距属性的部分 )
1.1 您可以为自身定义最小和最大尺寸ConstraintLayout
1.2 控件尺寸约束
android:layout_width可以通过 3 种不同方式设置和 android:layout_height属性 来指定控件的尺寸:
重要提示:
MATCH_PARENT不建议用于ConstraintLayout. 可以通过MATCH_CONSTRAINT将相应的左/右或上/下约束设置为来定义类似的行为"parent"。
WRAP_CONTENT (添加在 1 . 1中):强制约束
如果维度设置为WRAP_CONTENT,则在 1.1 之前的版本中,它们将被视为文字维度——也就是说,约束不会限制结果维度。虽然通常这已经足够(并且更快),但在某些情况下,您可能希望使用WRAP_CONTENT,但继续强制执行约束以限制结果维度。在这种情况下,您可以添加相应的属性之一:
MATCH_CONSTRAINT维度(添加在 1 . 1中)
当维度设置为MATCH_CONSTRAINT时,默认行为是让结果大小占用所有可用空间。有几个额外的修饰符可用:
layout_constraintWidth_min和layout_constraintHeight_min: 将设置此维度的最小尺寸
layout_constraintWidth_max和layout_constraintHeight_max: 将设置此维度的最大尺寸
layout_constraintWidth_percent和layout_constraintHeight_percent: 将此维度的大小设置为父维度的百分比
比率: 宽高比
您还可以将小部件的一个维度定义为另一个维度的比率。为此,您需要将至少一个约束维度设置为0dp(即MATCH_CONSTRAINT),并将属性设置layout_constraintDimensionRatio为给定的比率。例如:
除此之外,在设置宽高比的值的时候,还可以在前面加W或H,分别指定宽度或高度限制。 例如:
app:layout_constraintDimensionRatio="H,2:3"指的是 高:宽=2:3
app:layout_constraintDimensionRatio="W,2:3"指的是 宽:高=2:3
...
Guildline的主要属性:
Constraint 约束布局为了解决嵌套布局的弊端,更快的加载页面而出现,但是约束布局需要整体架构页面要有明确的构建页面的思维,故而学习以及思维模式要有的.所以个人感觉是简单页面还是用相对布局,线性布局就够了,对于复杂布局约束布局是你优化页面加载的不二之选.
*写作不容易,且赞且珍惜!!!*
Android启动优化概述
Android启动应用, 按 官方说法 分为冷启动, 温启动和热启动.
具体的定义可以看官方文档, 简单地说
一般我们只需要关注冷启动即可.
要想启动快, 硬件性能必然有影响, 在硬件一定的前提下, 我们要尽量 降低启动应用时CPU的负载 , 让CPU有更多的算力投入到启动流程中:
在做好一些基本原则后, 接着看具体的流程优化点
在应用进程创建后, 首先必然是加载类, 此时一些静态变量就会初始化了, 因此我们应该
类加载完毕后就是创建 Application 实例了, 因此我们应该
之后会先创建 ContentProvider 和执行 ContentProvider.onCreate() , 因此我们应该
跟接着就会执行 Application.onCreate() 等方法, 因此我们应该
接着就进入 Activity 环节.
同样第一步会是创建实例, 因此我们应该
在 Activity 进程生命周期后, 第一步就是渲染(inflate)布局, 我们应该
在应用启动的瞬间, 系统服务会先展示一个空白窗口, 等待应用第一帧绘制完毕后, 再从该窗口切换到应用, 如果启动耗时较长, 就会明显看到白屏, 对于这一点, 常见的操作有
可以使用IdleHandler, 在主线程空闲时再执行某些不重要的操作
实际上异步初始化只是不阻塞主线程, 但是子线程一样会占用CPU资源, 让主线程的执行时间变少, 所以不应该盲目地将所有工作放到子线程.
优化做到最后, 就是在系统流程上做文章了
原理是将启动时加载的类放到主dex,提升了这些类的内聚,让更多的类满足pre-verify的条件,在安装时就做了校验和优化,以减少首次加载的耗时,从而优化冷启动耗时。
Redex 初探与 Interdex:Andorid 冷启动优化
应用启动过程中会从apk压缩包中读取文件, 该优化的原理是利用Linux中的Pagecache机制, 让启动过程会用到的文件尽可能进入缓存中, 减少磁盘IO次数
支付宝 App 构建优化解析:通过安装包重排布优化 Android 端启动性能
在Dalvik VM(Android5.0以前)加载类的时候会有一个类校验过程, 它需要校验方法的每一个指令, 是一个比较耗时的过程, 可以通过Hook去掉类加载过程中的类验证过程. 不过对于ART(Android5.0之后)来说, 这个过程在安装时已经做了, 所以用处不大.
不进入冷启动, 就不用优化了~
这个Android Studio自带的工具, 可以看到启动过程中详细的方法执行流程, 但是采集数据本身会影响方法执行, 所以不能准确判断每个方法的耗时, 但是仍可以判断哪个方法相对来说耗时.
这个工具的好处是可以自定义事件, 可以指定需要采集的数据集, 可以看到线程间的状态等.
启动优化的一个关键点在于定义启动结束的点, 以及如何测量启动时间.
在Android4.4以上, 系统进程会提供一个类似 ActivityManager: Displayed ***: +3s534ms 的日志, 表示从启动进程到首次绘制完毕所用的时间.
应用可以在任何时候调用该方法, 触发系统打印类似 system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms 的日志
应用可以通过 ViewTreeObserver 来监听绘制前回调来判断第一帧的绘制时机, 或者直接在控件树的末尾加一个简单的View, 它 onDraw 调用时即表示页面(差不多)绘制完毕.
应用启动过程可以参考 Android Vitals Series' Articles 系列文章
关于Android布局你不知道的
Android常见的5个布局,我想大家一定不会陌生。LinearLayout、RelativeLayout和FrameLayout也是使用频率较高的布局方式,做Android开发的一定使用过。
传统的5种布局方式:
不过我的问题并不是问面试者如何使用这些基础的布局,而是要看面试者怎么解决布局嵌套(影响性能)和屏幕适配问题。
我们都清楚Android界面的布局太复杂,嵌套层次过深,会使整个界面的测量、布局和绘制变得更复杂,对性能会造成影响。所以我们在写Layout文件时,也要尽量避免布局的嵌套层次过深的问题。
在怎么解决问题之前,我们得有一个好方法先判断当前的问题情况。Android SDK工具箱中有一个叫做Hierarchy Viewer的工具,能够在App运行时分析Layout。
注意: 在ROOT的手机,或者是安装开发版的ROM的手机可以直接使用Hierarchy Viewer。如果没有Root的手机(SDK 4.1及以上),需要在你的PC端添加一个环境变量“ANDROID_HVPROTO=ddm”。
下面列举一些面试者常使用的方式。
merge merge标签的作用是合并UI布局,使用该标签能降低UI布局的嵌套层次。
merge标签可用于两种情况:
ViewStub ViewStub标签引入的布局默认不会inflate,既不会显示也不会占用位置。 ViewStub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如数据加载进度布局、出错提示布局等。
需要在使用时手动inflate:
ViewStub在一定的程度可以起到减少嵌套层次的作用,特别是很多时候我们的程序可能不需要走到ViewStub的界面。
include 将可复用的组件抽取出来并通过include标签使用,但include标签能减少布局的层次吗?
我认为不能。include主要解决的是相同布局的复用问题,它并不能减少布局的层次。
用RelativeLayout代替LinearLayout
很多人为了减少布局层次喜欢用RelativeLayout代替LinearLayout,不过可能达到的效果并不会很明显。层次是减少了,但本身RelativeLayout就会比LinearLayout性能差一点。
有一些界面,比如一个图片和一个文本的布局(ListItem常见的布局方式),可以利用TextView有drawableLeft, drawableRight等属性,完全不需要RelativeLayout或者LinearLayout布局。
传统的布局方式存在一定的缺陷,如RelativeLayout要两次测量(measure)它的子View才能知道确切的高度;如果LinearLayout布局的子View有设置了layout_weight,那么它也需要测量两次才能获得布局的高度。
相对于传统的布局方式,Android官方还推出了两种新的布局方式:ConstraintLayout和FlexboxLayout。
ConstraintLayout ConstraintLayout即约束布局,在2016年由Google I/O推出。ConstraintLayout和RelativeLayout有点类似,控件之间根据依赖关系而存在,但比RelativeLayout更加灵活。创建大型复杂的布局仍然可以使用扁平的层级(不用嵌套View Group),说的简单些就是,再复杂的界面也可以只有2层层次。
要使用ConstraintLayout需要在build.gradle中添加相关的support库:
使用ConstraintLayout可以有效的解决布局嵌套过多导致的性能问题,官方也对其渲染性能进行了优化,并且ConstraintLayout支持可视化的方式编写布局。
不过学会熟练使用ConstraintLayout会需要一点时间,但这是值得的。
FlexBoxLayout 做过前端开发(CSS方面)的同学对FlexBox一定不会陌生,最近我在做微信小程序开发时也涉及到FlexBox。FlexBox(弹性布局)是w3c在2009年提出的一种新的布局方案,解决以前那种传统css的盒模型的局限性。
Google开源了FlexboxLayout布局和前端CSS FlexBox布局具有相同的功能(肯定有不一样的地方),但已经足够在Android上改进布局的构建方式。
FlexBoxLayout可以理解成一种更高级的LinearLayout,不过比LinearLayout更加强大和灵活。如果我们使用LinearLayout布局的话,那么不同的分辨率,也许我们要重新调整布局,势必会需要跟多的布局文件放在不同的资源目录。而使用FlexBoxLayout来布局的话,它可以适应各种界面的改变(所以叫响应式布局)。
如果对前端的Flexbox不太了解的话,你还需要补一些概念,好在这些东西在网上很容易找到。
可能很多读者会觉这样的面试题是吹毛求疵,很多项目中哪有这么复杂的界面,根本就用不到这些优化措施。
可以说厉害的人,或者叫高手,可能只是比较多在意这些细节而已。在实践中的经历告诉我,很多难于解决的性能问题,并不是因为有一个影响性能的问题无法攻克,而是没有一个明显的制约因素,是有各种小问题一点一点堆积起来,最终积重难返。
所以,把细节做好,或者意识到细节的地方可能引发的问题,对我们解决问题是很有帮助的,不要浪费了让你可以成长的细节。
有需要更多Android高级进阶和面试资料的朋友可以私信我获取
布局优化方法
布局诱惑就是减少层级嵌套,减少overdraw。越简单越好
1、善用相对布局Relativelayout
在RelativeLayout和LinearLayout同时能够满足需求时,尽量使用RelativeLayout,因为可以通过扁平的RelativeLayout降低LinearLayout嵌套所产生布局树的层级。
可以使用AS自带工具协助优化布局---- Hierarchy View ,
用法:打开需要获取view层级的那个界面,然后依次点击菜单Tools - Android - Android Device Monitor。
2.2、merge标签
merge标签是作为include标签的一种辅助扩展来使用,它的主要作用是为了防止在引用布局文件时产生多余的布局嵌套。如:
这样提取出来后,使用上述的include标签引用布局时,就可以减少一个布局的嵌套了。而这一布局的子View的父层级直接是使用include引用的布局
2.3、viewstub标签
viewstub是view的子类。他是一个轻量级View, 隐藏的,没有尺寸的View。他可以用来在程序运行时简单的填充布局文件。同时他也有一个layout属性,当它隐藏时,不会显示任何布局和占据任何空间。当它显示时,就会用layout内的布局替代显示layout内的布局。
这里的ViewStub控件的layout指定为item_test_linear_layout。当点击button隐藏的时候不会显示item_test_linear_layout,而点击button显示的时候就会用item_test_linear_layout替代ViewStub。
3、使用最新布局ConstaintLayout
ConstraintLayout允许你在不适用任何嵌套的情况下创建大型而又复杂的布局。它与RelativeLayout非常相似,所有的view都依赖于兄弟控件和父控件的相对关系。
具体的使用可以查看相关的文档: ConstaintLayout使用
4、Android Lint工具寻求可能优化布局的层次
一些Lint规则如下:
1、使用组合控件: 包含了一个ImageView以及一个TextView控件的LinearLayout如果能够作为一个组合控件将会被更有效的处理。即自定义控件类型
2、合并作为根节点的帧布局(Framelayout) :如果一个帧布局是布局文件中的根节点,而且它没有背景图片或者padding等,更有效的方式是使用merge标签替换该Framelayout标签 。
3、无用的叶子节点:通常来说如果一个布局控件没有子视图或者背景图片,那么该布局控件是可以被移除(由于它处于 invisible状态)。
4、无用的父节点 :如果一个父视图即有子视图,但没有兄弟视图节点,该视图不是ScrollView控件或者根节点,并且它没有背景图片,也是可以被移除的,移除之后,该父视图的所有子视图都直接迁移至之前父视图的布局层次。同样能够使解析布局以及布局层次更有效。
5、过深的布局层次:内嵌过多的布局总是低效率的。考虑使用一些扁平的布局控件,例如 RelativeLayout、GridLayout ,来改善布局过程。默认最大的布局深度为10 。
文章出处: 更详细文章: Android性能优化之布局优化
网页名称:android布局优化,Android 优化
文章链接:http://azwzsj.com/article/dscdcch.html