Android 性能优化---布局优化

Android 布局绘制原理

布局加载过程

setContentView() --> inflate() -- > getLayout()(I/O操作) --> createViewFromTag() --> mFactory2/mFactory -- > onCreateView()(反射)

先看看源码

从setContentView(R.layout.activity_main);进入

public void setContentView(int resId) {

this.ensureSubDecor();

ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);

contentParent.removeAllViews();

LayoutInflater.from(this.mContext).inflate(resId, contentParent);

this.mOriginalWindowCallback.onContentChanged();

}

接着进入到inflate();

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {

final Resources res = getContext().getResources();

if (DEBUG) {

Log.d(TAG, "INFLATING from resource: "" + res.getResourceName(resource) + "" ("

+ Integer.toHexString(resource) + ")");

}

    final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}

这里主要是通过getLayout来获取到XmlResourceParser ,而这里面是进行大量的I/O操作

继续进入到inflate();这里方法比较多,就拿关键部分代码

这里面有一个createViewFromTag();根据命名可以知道,应该是根据标签来创建对应的view

...
//省略若干
try {
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
} if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
} if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
} return view;
...
//省略若干

从这里面可以知道主要是通过mFactory2 或者mFactory来创建,进入onCreateView();

....//省略若干
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name);
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null; try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, name); if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class); if (mFilter != null && clazz != null) {
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
if (mFilter != null) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(name);
if (allowedState == null) {
// New class -- remember whether it is allowed
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class); boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
}
}
}
....//省略若干

从这里就可以知道这里创建View的时候,是通过反射来进行的。

上面大概的流程,出现了两个可以优化的点,一个是I/O操作,一个是反射,如果一个布局文件嵌套很深,很复杂,这两个操作是相当的耗时的。那具体怎么优化呢,我们后面会说到。

CPU

CPU主要是负责计算显示的内容

GPU

GPU主要是负责栅格化,(UI元素绘制到屏幕上)

常用的优化工具

Systrace

systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况等。在Android平台中,它主要由3部分组成:

内核部分:Systrace利用了Linux Kernel中的ftrace功能。所以,如果要使用systrace的话,必须开启kernel中和ftrace相关的模块。

数据采集部分:Android定义了一个Trace类。应用程序可利用该类把统计信息输出给ftrace。同时,Android还有一个atrace程序,它可以从ftrace中读取统计信息然后交给数据分析工具来处理。

数据分析工具:Android提供一个systrace.py(python脚本文件,位于Android SDK目录/sdk/platform-tools/systrace中,其内部将调用atrace程序)用来配置数据采集的方式(如采集数据的标签、输出文件名等)和收集ftrace统计数据并生成一个结果网页文件供用户查看。

简单来说,当机器以60帧/秒显示(也就是16.6 ms),用户会感觉机器会流畅。如果出现显示时出现丢帧的情况,就需要知道系统在做什么?

Systrace 是用来收集系统和应用的数据信息和一些中间生成数据的细节,在Android 4.1系统和4.1之后的系统。

Systrace在一些分析显示的问题上特别有用,如应用画图慢,显示动作或动画时变形。

Systrace的使用

很多网上的文章说通过Tools -> Android -> Android Device Monitor 来打开这个Systrace,我没有具体去了解,Android studio从哪个版本开始,已经没法从Tools中打开了,我是通过以下方式打开的:

找到自己的SDK文件夹,可以看AS中的SDK路径

接着

接着

接着

打开即可

然后选择对应的进程

生成对应的html文件即可

生成对应的html文件在goole浏览器打开

Systrace 可以分析内存,卡顿,界面耗时等情况,这里主要说一下界面耗时分析方式

基本操作:

'w'按键:放大

's'按键:缩小

'd'按键:向右平移

'a'按键:向左平移

‘m’按钮,查看对应的控件渲染耗时时间

颜色区分:绿色表示正常,红色或者黄色表示丢帧,需要我们去优化,而出现红色或者黄色,则可以通过Alerts来查看详情。

Layout Inspector

布局的嵌套,我们可以通过Layout Inspector来进行查看

在Android studio中通过Tools ---> Layout Inspector进行打开

可以通过View Tree来查看布局的嵌套情况。

获取界面布局耗时

常规方式,手动埋点

这种方式比较简单,就是在setContentView();前后添加日志

AOP/ArtHook方式

ARTHook这种方式在前面启动优化已经介绍过了,可以回头看看 [https://www.cnblogs.com/huangjialin/p/13292042.html]

布局优化方式

AsyncLayoutInflater

AsyncLayoutInflater 这是谷歌提供的一个异步加载布局的一个类,使用这个方法,只是一个缓解,并没有从根本上解决耗时问题。

具体的,大家可以去深入了解

缺陷:

1、不能设置LayoutInflater.Factory()

2、注意View中不能有依赖主线程的操作。

其实我个人认为,这种方式并不好,由于是异步加载布局,那么如果主线程中或者在onResume();方法中有用到某个控件,那都是会出问题。(如果大家有什么好的想法,也可以评论)

X2C 框架

前面我们看了布局的加载过程可以知道,解析布局出现耗时,是由于布局过于复杂,导致有大量的I/O操作和反射操作,这是两个耗时的点。那么如果不直接在java文件写布局呢,是不是就避免了这些I/O和反射操作呢?可以的,但是在java文件上写布局,又带来一个问题,就是可维护性问题。

下面介绍这个框架,就是可以解决上面两个问题的

X2C框架

1、保留XML的优点,解决了其性能问题

2、原理:APT编译期翻译XML为java代码

使用方式

添加依赖

dependencies {
annotationProcessor 'com.zhangyue.we:x2c-apt:1.1.2'
implementation 'com.zhangyue.we:x2c-lib:1.0.6'
}

在对应的activity中添加注解

将onCreate();方法中的setContentView(R.layout.activity_test);更换为X2C.setContentView(TestActivity.this, R.layout.activity_test);

编译后可以在build -- generated -- ap_generated_sources -- debug -- 中查看生产对应的文件。

这个框架可以在编译期间帮我们将布局xml文件动态生成java文件,避免了前面说的解析布局文件出现的I/O和反射导致耗时的问题。

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

布局优化层级及复杂度

1、使用ConstraintLayout布局,减少层级嵌套。

2、不嵌套使用RelativeLayout,RelativeLayout和LinearLayout相比,LinearLayout性能更好,由于RelativeLayout是相对布局,确定位置的需要测量两次,性能较差。

3、不在嵌套LinearLayout中使用weight,LinearLayout中使用weight在onMeasure阶段会绘制两次,嵌套中使用,性能更差。

4、使用meger标签减少层级

避免过度绘制

1、去掉多余背景色,减少复杂shape使用

2、避免层级叠加

其他

1、viewstub:高效占位符,延迟初始化

2、onDraw()避免创建大对象,耗时操作

Android 性能优化---布局优化的更多相关文章

  1. Android性能优化---布局优化

    我们从事Android开发编写布局的时候大多数是使用XML来布局,这给我们带来了方便性,这样操作可以布局界面的代码和逻辑控制的Java代码分离出来,使程序的结构更加清晰.明了.特别的复杂的布局,但是这 ...

  2. android view:布局优化

    今天在图书馆看了一个android性能优化. 关于布局优化有几个小技巧: 1.尽量减少布局的嵌套,而使用相对布局,这样的话会减少布局对象的创建,并且可以再事件传递的时候减少传递嵌套. 2.使用incl ...

  3. Android中的布局优化方法

    http://blog.csdn.net/rwecho/article/details/8951009 Android开发中的布局很重要吗?那是当然.一切的显示样式都是由这个布局决定的,你说能不重要吗 ...

  4. Android开发之布局优化

    1.抽象布局标签 (1) <include>标签 include标签经常使用于将布局中的公共部分提取出来供其它layout共用,以实现布局模块化.这在布局编写方便提供了大大的便利. 以下以 ...

  5. [整]Android开发优化-布局优化

    优化布局层次结构 一个普遍的误解就是,使用基本的布局结构会产生高效的布局性能.然而每一个添加到应用的控件和布局,都需要初始化,布局位置和绘制.比如,使用一个嵌套的LinearLayout会导致过深的布 ...

  6. 关于android性能,内存优化

    转:http://www.starming.com/index.php?action=plugin&v=wave&tpl=union&ac=viewgrouppost& ...

  7. android 性能分析、优化

    .主要介绍了一些分析工具,比如GT.ITest等http://www.jianshu.com/p/8b77d394b2a6 .详细介绍啦android平台常见性能优化工具http://blog.csd ...

  8. 我的Android进阶之旅------>Android中的布局优化 include、merge 、ViewStub

    1.如何重用布局文件? 可以使用<include>标签引用其他的布局文件,并用android:id属性覆盖被引用布局文件中顶层节点的android:id属性值.代码如下: <!--引 ...

  9. Android性能优化之中的一个 布局优化

    本文为Android性能优化--布局优化,主要介绍使用抽象布局标签(include, viewstub, merge).去除不必要的嵌套和View节点.降低不必要的infalte及其它Layout方面 ...

随机推荐

  1. c语音学习笔记

    1.学习教程参考了杨光福 android jni Android视频<JNI> http://edu.csdn.net/course/detail/3235/54186?auto_star ...

  2. trollcave解题

    这是第一次完整地进行模拟渗透,前前后后一共花了一天时间,花了点时间写了个writeup. 博主是个菜鸡,如果有大神看到,请轻喷...... writeup下载:https://hrbeueducn-m ...

  3. Idea配置JRebel插件的详细配置及图解

    Idea最新JRebel插件的详细配置及图解 地址:https://blog.csdn.net/nyotengu/article/details/80629631#commentBox Ⅰ安装jreb ...

  4. HTML&CSS面试高频考点(二)

    HTML&CSS面试高频考点(一)    ♥ 6. W3C盒模型与怪异盒模型 标准盒模型(W3C标准) 怪异盒模型(IE标准) 怪异盒模型下盒子的大小=width(content + bord ...

  5. 浅谈bfs

    广搜(bfs) 定义 广度优先算法,简称BFS.是一种图形搜索演算法,简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点,如果发现目标,终止. 与dfs的相似之处与不同 结合深搜理解 相同点:都 ...

  6. css伪选择器使用总结——css中关于伪类和伪元素的知识总汇

    CSS 伪类用于向某些选择器添加特殊的效果,而CSS引入伪类和伪元素的概念是为了实现基于文档树之外的信息的格式化.这里讲总结关于css伪类和伪元素的相关使用 伪元素 :before/:before 在 ...

  7. Vue---day05

    目录 2. 客户端项目搭建 2.1 创建项目目录 2.2 初始化项目 2.3 安装路由vue-router 2.3.1 下载安装路由组件 2.3.2 配置路由 2.3.2.1 初始化路由对象 2.3. ...

  8. Spring 5.2.x 源码环境搭建(Windows 系统环境下)

    前期准备 1.确保本机已经安装好了 Git 2.Jdk 版本至少为 1.8 3.安装好 IntelliJ IDEA (其他开发工具,如 eclipse.Spring Tool Suite 等也是可以的 ...

  9. 使用Python进行XML解析

    XML 指可扩展标记语言(eXtensible Markup Language),常被设计用来传输和存储数据. 在进行医学图像标注时,我们常使用XML格式文件来存储标注,以下展示了使用Python来提 ...

  10. matlab 打包exe

    mcc -m gui_abc.m https://blog.csdn.net/hujiameihuxu/article/details/53525373 deploytool app compiler