我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何优化android布局

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何优化android布局

如何优化android布局?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

Android是什么

Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。

为什么要进行布局优化?

为什么要进行布局优化?答案是显而易见的,如果布局嵌套过深,或者其他原因导致布局渲染性能不佳,可能会导致应用卡顿 那么布局到底是如何导致渲染性能不佳的呢?首先我们应该了解下android绘制原理与布局加载原理

android绘制原理

Android的屏幕刷新中涉及到最重要的三个概念(为便于理解,这里先做简单介绍)

  • CPU:执行应用层的measure、layout、draw等操作,绘制完成后将数据提交给GPU

  • GPU:进一步处理数据,并将数据缓存起来

  • 屏幕:由一个个像素点组成,以固定的频率(16.6ms,即1秒60帧)从缓冲区中取出数据来填充像素点

总结一句话就是:CPU 绘制后提交数据、GPU 进一步处理和缓存数据、最后屏幕从缓冲区中读取数据并显示

如何优化android布局

双缓冲机制

看完上面的流程图,我们很容易想到一个问题,屏幕是以16.6ms的固定频率进行刷新的,但是我们应用层触发绘制的时机是完全随机的(比如我们随时都可以触摸屏幕触发绘制). 如果在GPU向缓冲区写入数据的同时,屏幕也在向缓冲区读取数据,会发生什么情况呢?有可能屏幕上就会出现一部分是前一帧的画面,一部分是另一帧的画面,这显然是无法接受的,那怎么解决这个问题呢?

所以,在屏幕刷新中,Android系统引入了双缓冲机制

GPU只向Back Buffer中写入绘制数据,且GPU会定期交换Back Buffer和Frame Buffer,交换的频率也是60次/秒,这就与屏幕的刷新频率保持了同步。

如何优化android布局

虽然我们引入了双缓冲机制,但是我们知道,当布局比较复杂,或设备性能较差的时候,CPU并不能保证在16.6ms内就完成绘制数据的计算,所以这里系统又做了一个处理。当你的应用正在往Back Buffer中填充数据时,系统会将Back Buffer锁定。如果到了GPU交换两个Buffer的时间点,你的应用还在往Back Buffer中填充数据,GPU会发现Back Buffer被锁定了,它会放弃这次交换。

这样做的后果就是手机屏幕仍然显示原先的图像,这就是我们常常说的掉帧

布局加载原理

由上面可知,导致掉帧的原因是CPU无法在16.6ms内完成绘制数据的计算。而之所以布局加载可能会导致掉帧,正是因为它在主线程上进行了耗时操作,可能导致CPU无法按时完成数据计算

布局加载主要通过setContentView来实现,我们就不在这里贴源码了,一起来看看它的时序图

如何优化android布局

我们可以看到,在setContentView中主要有两个耗时操作

  • 解析xml,获取XmlResourceParser,这是IO过程

  • 通过createViewFromTag,创建View对象,用到了反射

以上两点就是布局加载可能导致卡顿的原因,也是布局的性能瓶颈

获取布局文件加载耗时的方法

我们如果需要优化布局卡顿问题,首先最重要的就是:确定定量标准 所以我们首先介绍几种获取布局文件加载耗时的方法

常规获取

首先介绍一下常规方法

val start = System.currentTimeMillis()setContentView(R.layout.activity_layout_optimize)val inflateTime = System.currentTimeMillis() - start

这种方法很简单,因为setContentView是同步方法,如果想要计算耗时,直接将前后时间计算相减即可得到结果了

AOP(Aspectj,ASM)

上面的方式虽然简单,但是却不够优雅,同时代码有侵入性,如果要对所有Activity测量时,就需要在基类中复写相关方法了,比较麻烦了 下面介绍一种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("aop inflate",name + " cost " + (System.currentTimeMillis() - time));    }

上面用的Aspectj,比较简单,上面的注解的意思是在setContentView方法执行内部去调用我们写好的getSetContentViewTime方法 这样就可以获取相应的耗时 我们可以看下打印的日志

I/aop inflate: AppCompatActivity.setContentView(..) cost 69
I/aop inflate: AppCompatActivity.setContentView(..) cost 25

这样就可以实现无侵入的监控每个页面布局加载的耗时 具体源码可见文末

获取任一控件耗时

有时为了更精确的知道到底是哪个控件加载耗时,比如我们新添加了自定义View,需要监控它的性能 我们可以利用setFactory2来监听每个控件的加载耗时 首先我们来回顾下setContentView方法

public final View tryCreateView(@Nullable View parent, @NonNull String name,        ...        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;        }        ...        return view;    }

在真正进行反射实例化xml结点前,会调用mFactory2的onCreateView方法 这样如果我们重写onCreateView方法,在其前后加上耗时统计,即可获取每个控件的加载耗时

private fun initItemInflateListener(){        LayoutInflaterCompat.setFactory2(layoutInflater, object : Factory2 {            override fun onCreateView(                parent: View?,                name: String,                context: Context,                attrs: AttributeSet            ): View? {                val time = System.currentTimeMillis()                val view = delegate.createView(parent, name, context, attrs)                Log.i("inflate Item",name + " cost " + (System.currentTimeMillis() - time))                return view            }            override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {                return null            }        })    }

如上所示:真正的创建View的方法,仍然是调用delegate.createView,我们只是其之前与之后做了埋点 注意,initItemInflateListener需要在onCreate之前调用 这样就可以比较方便地实现监听每个控件的加载耗时

布局加载优化的一些方法介绍

布局加载慢的主要原因有两个,一个是IO,一个是反射 所以我们的优化思路一般有两个

  1. 侧面缓解(异步加载)

  2. 根本解决(不需要IO,反射过程,如X2C,Anko,Compose等)

AsyncLayoutInflater方案

AsyncLayoutInflater 是来帮助做异步加载 layout 的,inflate(int, ViewGroup, OnInflateFinishedListener) 方法运行结束之后 OnInflateFinishedListener 会在主线程回调返回 View;这样做旨在 UI 的懒加载或者对用户操作的高响应。

简单的说我们知道默认情况下 setContentView 函数是在 UI 线程执行的,其中有一系列的耗时动作:Xml的解析、View的反射创建等过程同样是在UI线程执行的,AsyncLayoutInflater 就是来帮我们把这些过程以异步的方式执行,保持UI线程的高响应。

使用如下:

@Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        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);                    }                });        // 别的操作    }

这样做的优点在于将UI加载过程迁移到了子线程,保证了UI线程的高响应 缺点在于牺牲了易用性,同时如果在初始化过程中调用了UI可能会导致崩溃

X2C方案

X2C是掌阅开源的一套布局加载框架 它的主要是思路是在编译期,将需要翻译的layout翻译生成对应的java文件,这样对于开发人员来说写布局还是写原来的xml,但对于程序来说,运行时加载的是对应的java文件。这就将运行时的开销转移到了编译时 如下所示,原始xml文件:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:app="http://schemas.android.com/apk/res-auto"  xmlns:tools="http://schemas.android.com/tools"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:paddingLeft="10dp">  <include      android:id="@+id/head"      layout="@layout/head"      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:layout_centerHorizontal="true" />  <ImageView      android:id="@+id/ccc"            android:layout_below="@id/head" /></RelativeLayout>

X2C 生成的 Java 文件

public class X2C_2131296281_Activity_Main implements IViewCreator {  @Override  public View createView(Context ctx, int layoutId) {        Resources res = ctx.getResources();        RelativeLayout relativeLayout0 = new RelativeLayout(ctx);        relativeLayout0.setPadding((int)(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,res.getDisplayMetrics())),0,0,0);        View view1 =(View) new X2C_2131296283_Head().createView(ctx,0);        RelativeLayout.LayoutParams layoutParam1 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);        view1.setLayoutParams(layoutParam1);        relativeLayout0.addView(view1);        view1.setId(R.id.head);        layoutParam1.addRule(RelativeLayout.CENTER_HORIZONTAL,RelativeLayout.TRUE);        ImageView imageView2 = new ImageView(ctx);        RelativeLayout.LayoutParams layoutParam2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,(int)(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,res.getDisplayMetrics())));        imageView2.setLayoutParams(layoutParam2);        relativeLayout0.addView(imageView2);        imageView2.setId(R.id.ccc);        layoutParam2.addRule(RelativeLayout.BELOW,R.id.head);        return relativeLayout0;  }}

使用时如下所示,使用X2C.setContentView替代原始的setContentView即可

// this.setContentView(R.layout.activity_main);X2C.setContentView(this, R.layout.activity_main);

X2C优点

  1. 在保留xml的同时,又解决了它带来的性能问题

  2. 据X2C统计,加载耗时可以缩小到原来的1/3

X2C问题

  1. 部分属性不能通过代码设置,Java不兼容

  2. 将加载时间转移到了编译期,增加了编译期耗时

  3. 不支持kotlin-android-extensions插件,牺牲了部分易用性

Anko方案

Anko是JetBrains开发的一个强大的库,支持使用kotlin DSL的方式来写UI,如下所示

class MyActivity : AppCompatActivity() {    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {        super.onCreate(savedInstanceState, persistentState)        MyActivityUI().setContentView(this)    }}class MyActivityUI : AnkoComponent<MyActivity> {    override fun createView(ui: AnkoContext<MyActivity>) = with(ui) {        verticalLayout {            val name = editText()            button("Say Hello") {                onClick { ctx.toast("Hello, ${name.text}!") }            }        }    }}

如上所示,Anko使用kotlin DSL实现布局,它比我们使用Java动态创建布局方便很多,主要是更简洁,它和拥有xml创建布局的层级关系,能让我们更容易阅读 同时,它去除了IO与反射过程,性能更好,以下是Anko与XML的性能对比

如何优化android布局

不过由于AnKo已经停止维护了,这里不建议大家使用,了解原理即可 AnKo建议大家使用Jetpack Compose来替代使用

Compose方案

Compose 是 Jetpack 中的一个新成员,是 Android 团队在2019年I/O大会上公布的新的UI库,目前处于Beta阶段 Compose使用纯kotlin开发,使用简洁方便,但它并不是像Anko一样对ViewGroup的封装 Compose 并不是对 View 和 ViewGroup 这套系统做了个上层包装来让写法更简单,而是完全抛弃了这套系统,自己把整个的渲染机制从里到外做了个全新的。

可以确定的是,Compose是取代XML的官方方案

Compose的主要优点就在于它的简单好用,具体来说就是两点

  1. 它的声明式 UI

  2. 去掉了 xml,只使用 Kotlin 一种语言

由于本文并不是介绍Compose的,所以就不继续介绍Compose了,总得来说,Compose是未来android UI开发的方向,读者可以自行查阅相关资料

一些常规优化手段

上面介绍了一些改动比较大的方案,其实我们在实际开发中也有些常规的方法可以优化布局加载 比如优化布局层级,避免过度绘制等,这些简单的手段可能正是可以应用到项目中的

优化布局层级及复杂度

  1. 使用ConstraintLayout,可以实现完全扁平化的布局,减少层级

  2. RelativeLayout本身尽量不要嵌套使用

  3. 嵌套的LinearLayout中,尽量不要使用weight,因为weight会重新测量两次

  4. 推荐使用merge标签,可以减少一个层级

  5. 使用ViewStub延迟加载

避免过度绘制

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

  2. 避免层级叠加

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

关于如何优化android布局问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网行业资讯频道了解更多相关知识。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何优化android布局

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何优化android布局

如何优化android布局?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Android是什么Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移
2023-06-14

Android布局优化之ViewStub控件

ViewStub是Android布局优化中一个很不错的标签/控件,直接继承自View。虽然Android开发人员基本上都听说过,但是真正用的可能不多。 ViewStub可以理解成一个非常轻量级的View,与其他的控件一样,有着自己的属性及特
2022-06-06

Android中ListView Item布局优化技巧

本文实例讲述了Android中ListView Item布局优化技巧。分享给大家供大家参考,具体如下: 之前一直都不知道ListView有多种布局的优化方法,只能通过隐藏来实现,自己也知道效率肯定是很低的,但是也不知道有什么方法,这些天又查
2022-06-06

如何优化Discuz导航栏布局?

如何优化Discuz导航栏布局?Discuz是一款功能强大的开源论坛系统,广泛应用于各类网站。在网站建设过程中,导航栏是至关重要的部分,它直接影响用户体验和网站的整体布局效果。本文将介绍如何优化Discuz导航栏布局,并提供具体的代码示例
如何优化Discuz导航栏布局?
2024-03-02

CSS Positions布局优化指南:如何减少布局刷新

在网页开发中,布局是一个非常重要的部分。合理的布局不仅能提升用户体验,还能优化网页的性能。其中,CSS Positions是实现各种布局效果不可或缺的一种技术。然而,使用不当可能导致页面频繁刷新,进而影响网页性能和用户体验。本文将介绍一些有
2023-10-21

Android中使用ViewStub实现布局优化

在Android开发中,View是我们必须要接触的用来展示的技术.通常情况下随着View视图的越来越复杂,整体布局的性能也会随之下降.这里介绍一个在某些场景下提升布局性能的View,它就是ViewStub. ViewStub是什么ViewS
2022-06-06

android布局优化的技巧有哪些

以下是一些优化Android布局的技巧:1. 使用ConstraintLayout:ConstraintLayout可以帮助创建复杂的布局,减少嵌套和提高性能。2. 使用LinearLayout代替RelativeLayout:Linear
2023-10-09

Android中卡顿优化布局实例分析

小编今天带大家了解Android中卡顿优化布局实例分析,文中知识点介绍的非常详细。觉得有帮助的朋友可以跟着小编一起浏览文章的内容,希望能够帮助更多想解决这个问题的朋友找到问题的答案,下面跟着小编一起深入学习“Android中卡顿优化布局实例
2023-06-28

Android自定义实现BaseAdapter的优化布局

上一篇中我们介绍了自定义实现BaseAdapter的普通实现布局,然而上一章也说了普通实现的方式效率会很低,而且对系统开销也很大,所以,那样的实现是为了让初学者能知道可以这样使用,在实际项目中不可能使用那种方式的,要是你在做项目的时候使用普
2022-06-06

Android开发之merge结合include优化布局

merge结合include优化android布局,效果不知道,个人感觉使用上也有很大的局限,不过还是了解一下,记录下来。 布局文件都要有根节点,但android中的布局嵌套过多会造成性能问题,于是在使用include嵌套的时候我们可以使用
2022-06-06

CSS网格布局优化:优化网页布局的性能和效果

CSS网格布局优化:优化网页布局的性能和效果,需要具体代码示例在前端开发中,网页布局是至关重要的一部分。CSS网格布局(CSS Grid Layout)是一种强大的布局模型,它能够帮助开发者更高效地构建网页布局,并进一步优化网页的性能和效果
CSS网格布局优化:优化网页布局的性能和效果
2023-11-18

Android布局性能优化之按需加载View

有时应用程序中会有一些很少用到的复杂布局。在需要它们的时候再加载可以降低内存的消耗,同时也可以加快界面的渲染速度。 定义ViewStub ViewStub是一个轻量级的View,它没有高宽,也不会绘制任何东西。所以它的加载与卸载的成本很低。
2022-06-06

Android线性布局与相对布局如何实现

这篇文章主要讲解了“Android线性布局与相对布局如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android线性布局与相对布局如何实现”吧!线性布局(LinearLayout)名
2023-06-29

android水平布局和垂直布局如何嵌套

在Android中,可以使用水平布局和垂直布局来实现嵌套布局。下面是一个示例代码,展示了如何嵌套水平布局和垂直布局:```xmlandroid:layout_width="match_parent"android:layout_height
2023-10-10

android如何实现自由布局

Android中可以使用相对布局(RelativeLayout)来实现自由布局。相对布局允许控件相对于其他控件或父容器进行布局。以下是实现自由布局的步骤:1. 在XML布局文件中,使用RelativeLayout作为根容器。2. 在Rela
2023-10-10

CSS Positions布局优化指南:减少布局重绘的方法

在前端开发的过程中,布局重绘(Layout Repaint)是一项常见的性能问题。当页面元素的位置、尺寸或显示状态发生改变时,浏览器需要重新计算并绘制页面布局,这会消耗大量的计算资源,导致页面的加载和响应速度变慢。为了提升页面的性能,我们需
2023-10-21

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录