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. 第三模块 :微服务网关Zuul架构和实践

    52.Zuul网关架构剖析~1.mp4 请求过来之后首先会通过前置过滤器,然后到路由过滤器,路由过滤器是真正访问后台远程服务的,经过路由器之后,最后会传递给后置过滤器 在三个过滤器处理的过程中任何一个 ...

  2. JavaWeb网上图书商城完整项目--day02-8.提交注册表单功能之dao、service实现

    1.发送邮件 发送邮件的时候的参数我们都写在了配置文件中,配置文件放在src目录下,可以使用类加载器进行加载该数据 //向注册的用户发送邮件 //1读取配置文件 Properties properti ...

  3. vmware 虚拟机安装失败如何解决

    1.最好安装在默认路径2,安装之前先卸载之前安装的软件,卸载使用最经典的Windows软件卸载工具Windows install clean up其他方式的卸载我使用了很多次都不行,网上很多方法都看了 ...

  4. 基于 Blazor 开发五子棋⚫⚪小游戏

    今天是农历五月初五,端午节.在此,祝大家端午安康! 端午节是中华民族古老的传统节日之一.端午也称端五,端阳.此外,端午节还有许多别称,如:午日节.重五节.五月节.浴兰节.女儿节.天中节.地腊.诗人节. ...

  5. Docker(五)Docker镜像讲解

    Docker镜像讲解 镜像概念 镜像是一种轻量级.可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码.运行时.库.环境变量和配置文件 Dock ...

  6. 入门大数据---通过Yarn搭建MapReduce和应用实例

    上一篇中我们了解了MapReduce和Yarn的基本概念,接下来带领大家搭建下Mapreduce-HA的框架. 结构图如下: 开始搭建: 一.配置环境 注:可以现在一台计算机上进行配置,然后分发给其它 ...

  7. Swoole 中 TCP、UDP 和长连接、短连接

    TCP 服务 swoole 文档 - TCP 服务 tcp 服务端 <?php // 1. 创建 swoole 默认创建的是一个同步的阻塞tcp服务 $host = "0.0.0.0& ...

  8. Netty 源码解析(三): Netty 的 Future 和 Promise

    今天是猿灯塔“365篇原创计划”第三篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel 当前:Ne ...

  9. tidyverse|数据分析常规操作-分组汇总(sumamrise+group_by)

    | 本文首发于 “生信补给站” https://mp.weixin.qq.com/s/tQt0ezYJj3H7x3aWZmKVEQ 使用tidyverse进行简单的数据处理: 盘一盘Tidyverse ...

  10. 如何实现 token 加密

    jwt举例 需要一个secret(随机数) 后端利用secret和加密算法(如:HMAC-SHA256)对payload(如账号密码)生成一个字符串(token),返回前端 前端每次request在h ...