Android绘制优化(二)布局优化

相关文章

Android绘制优化(一)绘制性能分析

前言

我们知道一个界面的测量和绘制是通过递归来完成的,减少布局的层数就会减少测量和绘制的时间,从而性能就会得到提升。当然这只是布局优化的一方面,那么如何来进行布局的分析和优化呢?本篇文章会给你一个满意的答案。

1.布局优化工具

在讲到如何去布局优化前,我们先来学习两种布局优化的工具。

Hierarchy Viewer

Hierarchy Viewer是Android SDK自带的可视化的调试工具,用来检查布局嵌套和绘制的时间。需要注意的是在在Android的官方文档中提到:出于安全考虑,Hierarchy Viewer只能连接Android开发版手机或是模拟器。

首先我们在Android Studio中选择Tools->Android->Android Device Monitor,在Android Device Monitor中选择Hierarchy Viewer ,如下图所示:

选择Hierarchy Viewer后会进出Hierarchy Viewer窗口,如下图所示。

Hierarchy Viewer中有4个四个子窗口,它们的的作用为:

- Windows:当前设备所有界面列表。

- Tree View:将当前Activity的所有View的层次按照高层到低层从左到右显示出来。

- Tree Overview:全局概览,以缩略的形式显示。

- Layout View:整体布局图,以手机屏幕上真实的位置呈现出来。单击某一个控件,会在Tree Overview窗口中显示出对应的控件。

根据上面讲到的Hierarchy Viewer的4个四个子窗口,我们可以很容易的查看我们布局控件的层级关系。当然Hierarchy Viewer还可以查看某一个View的耗时,我们可以选择某一个View,然后单击下图红色箭头标识的按钮,这里我们把他简称为Layout Time按钮。

从图中可以看出被选中的RelativeLayout自身的Measure、Layout和Draw的耗时数据都为n/a。单击Layout Time按钮后,就可以查看View的耗时情况了,如下图所示。

从图中可以看出,被选中的LinearLayout给出了自身Measure、Layout和Draw的耗时,并且它所包含的View中都有了三个指示灯,分别代表当前View在Measure、Layout和Draw的耗时,绿色代表比其他50%View的同阶段(比如Measure阶段)速度要快,黄色则代表比其他50%View同阶段速度要慢,红色则代表比其他View同阶段都要慢,则需要注意了。如果想要看View的具体耗时,则点击该View就可以了。

Android Lint

Android lint是在ADT 16提供的新工具,它是一个代码扫描工具,通过代码静态检查来发现代码出现的潜在问题,并给出优化建议。检查的范围主要有以下几点:

- Correctness 正确性

- Security 安全性

- Performance 性能

- Usability 可用性

- Accessibility 可达性

- Internationalization 国际化

Android Lint功能十分强大,这里我们只关注XML布局检查,我们可以通过Android Studio的Analyze->Inspect Code来配置检查的范围,如下图所示。

点击上图的OK按钮后,就会进行代码检查,检查的结果如下图所示。

图中列出了项目中出现的问题种类,以及每个问题种类的个数,问题种类包括我们前面提到的Correctness 、Internationalization 、Performance等。我们点击展开最后的XML一项,点击一个问题,就会出现如下图的提示。

可以看出给出了Namespace declaration is never used的提示,并指出了问题所在的文件和行数,我们点击数字3,直接跳入到问题的代码,发现如下代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    ...

第三行的Namespace 确实没有被用到。如果想要自定义Android Lint的检查提示,可以通过File->Settings->Editor->Inspections中来配置Android Lint,如下图所示。

从图中可以发现,我们可以配置Android Lint检查的范围以及问题的严重等级。

2.布局优化方法

布局的优化方法很多,主要包括合理运用布局、Include、Merge、ViewStub,下面我们来一一对这些内容进行讲解。

合理运用布局

我们常用的布局主要有LinearLayout、RelativeLayout和FrameLayout等,合理的使用它们可以使得Android绘制工作量变少,性能得到提高。我们来举个简单的例子。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="布局优化" />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Merge" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ViewStub" />
    </LinearLayout>
</LinearLayout>

上面的代码用了两个LinearLayout来进行布局,运行效果如下图所示。

我们用Hierarchy Viewer来查看层级情况,如下图所示。

可以看到我们的布局共有3层,一共含有5个View。如果我们用RelativeLayout来进行改写呢?代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv_text1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="布局优化" />
    <TextView
        android:id="@+id/tv_text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@id/tv_text1"
        android:text="Merge" />
    <TextView
        android:id="@+id/tv_text3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_text2"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/tv_text1"
        android:text="ViewStub" />
</RelativeLayout>

我们只用了一个RelativeLayout来进行布局。用Hierarchy Viewer来查看层级情况,如下图所示。

布局共有两层,一共含有4个View。从这里我们就可以看出我们用RelativeLayout减少了一层的布局,当然这只是一个简单例子,如果布局复杂,那么合理的用RelativeLayout来替代LinearLayout会减少很多层布局。

一般情况下,RelativeLayout的性能是比LinearLayout低,因为RelativeLayout中的View的排列方式是基于彼此依赖的。但是如果布局层数较多时,如果能用RelativeLayout来实现,还是推荐用RelativeLayout。

使用Include标签来进行布局复用

一个很常见的场景就是,多个布局需要复用一个相同的布局,比如一个TitleBar。如果这些界面都要加上这个相同布局TitleBar,维护起来就就很麻烦,我们需要复制TitleBar的布局到每个需要添加的界面,这样容易发生遗漏。如果需要修改TitleBar则需要去每个引用TitleBar的布局进行修改。为了解决这些问题,我们可以用Include标签来解决。

首先我们先来写一个简单的TitleBar布局:titlebar.xml 如下所示。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:background="@android:color/darker_gray">
    <ImageView
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:src="@drawable/ico_left"
        android:padding="3dp"
        android:layout_gravity="center"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="绘制优化" />
</LinearLayout>

这个TitleBar由ImageView和TextView组成,下面我们将TitleBar引入到我们此前用过的布局中,如下所示。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <include layout="@layout/titlebar" />
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="布局优化" />
     ...
    </RelativeLayout>
</LinearLayout>

可以看到我们用include标签引入了titlebar布局,运行效果如下图所示。

用Merge标签去除多余层级

Merge意味着合并,在合适的场景使用Merge标签可以减少多余的层级。Merge标签一般和Include标签搭配使用,上面的例子,我们用Hierarchy Viewer来查看布局层级,如下图所示。

可以看到我们用Include标签引用的布局的根布局是一个LinearLayout。如果我们使用Merge标签来替换LinearLayout呢?titlebar.xml 的代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:background="@android:color/darker_gray
   >
    <ImageView
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:src="@drawable/ico_left"
        android:padding="3dp"
        android:layout_gravity="center"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="绘制优化" />
</merge>

这时我们再用Hierarchy Viewer来查看布局层级,如下图所示。

可以看到此前的根布局LinearLayout没有了,但是我们用merge标签来替代LinearLayout导致LinearLayout失效,因此布局就错乱了,因此可以得知merge标签最好是来替代FrameLayout,或者是布局一致的LinearLayout,比如当前布局的LinearLayout是垂直方向的,被包含的布局的LinearLayout也是垂直方向的则可以用merge标签,本场景包含的LinearLayout是水平的,显然并不符合这一要求。但是如果执意想要在本场景使用merge标签也是可以的,就是用继承自LinearLayout的自定义View,代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <com.example.liuwangshu.moonlayout.TitleBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:background="@android:color/darker_gray">
    </com.example.liuwangshu.moonlayout.TitleBar>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
    <TextView
            android:id="@+id/tv_text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="布局优化" />
      ...
    </RelativeLayout>
</LinearLayout>

上面布局中TitleBar就是一个自定义View,它继承LinearLayout。我们在TitleBar标签中添加此前的LinearLayout的属性:android:orientation和android:background。

使用ViewStub来提高加载速度

一个很常见的开发场景就是我们想要一个布局时,并不是所有的控件都需要显示出来,而是显示出一部分,对于这种情况,我们一般采用的方法就是使用View的GONE和INVISIBLE,但是这种方法效率不高,虽然是达到了隐藏的目的,但是仍在布局当中,系统仍然会解析它们,我们可以用ViewStub来解决这一问题。

ViewStub是轻量级的View,不可见并且不占布局位置。当ViewStub调用inflate方法或者设置可见时,系统会加载ViewStub指定的布局,然后将这个布局添加到ViewStub中,因此,在对ViewStub调用inflate方法或者设置可见时,它是不占布局空间和系统资源的,它主要的目的就是为目标视图占用一个位置。因此,使用ViewStub可以提高界面初始化的性能,从而提高界面的加载速度。

我们首先在布局中加入ViewStub标签,布局代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
  <ViewStub
      android:id="@+id/viewsub"
      android:layout_width="match_parent"
      android:layout_height="40dp"
      android:layout="@layout/titlebar"/>
   ...
</LinearLayout>

ViewStub标签中用android:layout引用了此前写好的布局titlebar.xml。这时我们运行程序,ViewStub标签所引用的布局是显示不出来的,因为引该局还没有加载到ViewStub中,接下来在代码中使用ViewStub:

public class MainActivity extends AppCompatActivity {
    private ViewStub viewsub;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewsub= (ViewStub) findViewById(R.id.viewsub);
//        viewsub.inflate();//1
        viewsub.setVisibility(View.VISIBLE);//2
    }
}

可以使用注释1和注释2处的代码来将ViewStub引用的布局加载到ViewStub中,这样引用的布局就显示了出来。

在使用ViewStub时需要主要以下问题:

- ViewStub只能加载一次,加载后ViewStub对象会被置为空,这样当ViewStub引用的布局被加载后,就不能用ViewStub来控制引用的布局了。因此,如果一个控件需要不断的显示和隐藏,还是要使用View的Visibility属性。

- ViewStub不能嵌套Merge标签。

- ViewStub操作的是布局文件,如果只是想操作具体的View,还是要使用View的Visibility属性。

3.避免GPU过度绘制

什么是过度绘制呢?我们来打个比方,假设你要粉刷房子的墙壁,一开始刷了绿色,接着又刷了黄色,这样黄色就将绿色盖住,也就说明第一次的大量粉刷工作白做了。同样手机屏幕绘制也是如此,过度绘制是指在屏幕上某个像素在同一帧的时间内被绘制多次,从而浪费了GPU和CPU的资源。产生这一原因主要有两个原因:

- 在XML布局中,控件有重叠且都有设置背景。

- View的OnDraw中同一区域绘制多次。

过度绘制是不可避免的,但是过多的过度绘制会浪费很多资源,并且导致性能问题,因此,避免过度绘制是十分必要的。我们可以用Android系统中自带的工具来检测过度绘制。首先要保证系统版本在Android 4.1以上,接着在开发者选项中打开调试GPU过度绘制选项就可以进入GPU过度绘制模式,如下图所示。

这时屏幕会出现出各种颜色,主要有以下几种,如下图所示。

各个颜色的定义为:

- 原色: 没有过度绘制 – 每个像素在屏幕上绘制了一次。

- 蓝色: 一次过度绘制 – 每个像素点在屏幕上绘制了两次。

- 绿色: 两次过度绘制 – 每个像素点在屏幕上绘制了三次。

- 粉色: 三次过度绘制 – 每个像素点在屏幕上绘制了四次。

- 红色: 四次或四次以上过度绘制 – 每个像素点在屏幕上绘制了五次或者五次以上。

最理想的是蓝色,一个像素只绘制一次,合格的页面绘制是白色、蓝色为主,绿色以上区域不能超过整个的三分之一,颜色越浅越好。

避免过度绘制主要有以下几个方案:

1.移除不需要的background。

2.在自定义View的OnDraw方法中,用canvas.clipRect来指定绘制的区域,防止重叠的组件发生过度绘制。



欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Android相关原创技术干货。

扫一扫下方二维码或者长按识别二维码,即可关注。

时间: 2024-10-11 18:29:01

Android绘制优化(二)布局优化的相关文章

【转】Android性能优化之布局优化篇

转自:http://blog.csdn.net/feiduclear_up/article/details/46670433 Android性能优化之布局优化篇 分类: andorid 开发2015-06-29 16:28 238人阅读 评论(0) 收藏 举报 性能布局viewStubinclude布局优化 目录(?)[-] Hierarchy View检测布局嵌套层次 显示GPU过度绘制 懒加载布局 ViewStub Android Lint 工具 怎样才能写出优秀的Android App,是

Android 性能优化 三 布局优化ViewStub标签的使用

小黑与小白的故事,通过虚拟这两个人物进行一问一答的形式来共同学习ViewStub的使用 小白:Hi,小黑,ViewStub是什么?听说可以用来进行布局优化. 小黑:ViewStub 是一个隐藏的,不占用内存空间的视图对象,它可以在运行时延迟加载布局资源文件.(更多详细的API等信息可以查看官方文档ViewStub),计算机行业一向是实践里面出真知,下面用一个例子演示下效果. 小黑:说说概念只是为了概括性的了解下,还是用个实例来演示下.先来创建一个Activity中使用的布局文件,文件名是:act

Android性能优化之布局优化

由于Android系统对硬件的要求较高,并且上层应用都是用Java(效率要比C++低)编写的,对程序的优化就成了程序员的日常工作了:Android的优化 可以从以下几个地方下手:布局优化.数据库优化.使用异步加载数据.使用缓存技术.算法代码优化.使用线程池 先从比较简单的布局入手 程序目录结构 1.使用 <include>标签复用相同部分的布局文件,就是在一個而已文件中包含另一个布局 activity_main.xml <RelativeLayout xmlns:android=&quo

android应用程序优化之布局优化

在我们开发APP时不仅要在代码实现上.做到对App的优化,而在我们的界面布局也有很多要优化的地方,假设布局写的非常low的话,系统载入布局的速度会十分的慢,使得用户的体验非常的不好.这篇文章主要是从我平时对布局的优化方面总结一下,我觉得常常能够用到的布局优化方面的一些技巧和手段. 1.降低布局的嵌套.这一点也是最重要的 搞android的都知道,android的整个UI布局文件最后也是要一层一层的解析成View对象的,假设层次太深的话,对导致递归的层次太深而极大的影响解析速度,所以,我们一定不能

Android Studido下的应用性能优化总结--布局优化

前言:一个应用的成功=产品设计*性能 ,再此我们不讨论一个应用的设计,那交给我们可爱又可恨的产品经理和UI设计师来决定!所以这里分步骤讨论如何提升一个应用的性能,这里先探讨布局优化问题. 布局优化 避免过度回执(Overdraw),由于过度绘制会浪费很多的CPU,GPU资源, 检查方法: 通过打开开发者选项–>GPU模式呈现分析–>在屏幕上显示为条形图.然后就会看到如下图的情况,最好用真机,模拟器回执有问题.  你可以观察一下绘制的条形图 tips:每一条柱状线都包含三部分,蓝色代表测量回执D

Android性能优化之一 布局优化

本文为Android性能优化--布局优化,主要介绍使用抽象布局标签(include, viewstub, merge).去除不必要的嵌套和View节点.减少不必要的infalte及其他Layout方面可调优点,顺带提及布局调优相关工具(hierarchy viewer和lint). 一.布局复杂度的优化 关于布局的优化,主要分两个大方向 实现相同界面效果并且层级结构相同时,选用何种Layout最好 在Android中单独的布局性能: FrameLayout>LinearLayout>Relat

Android性能优化之布局优化篇

怎样才能写出优秀的Android App,是每一个程序员追求的目标.那么怎么才能写出一个优秀的App呢?相信很多初学者也会有这种迷茫.一句话来回答这个问题:细节很重要.今天我们就从最基础的XML布局来谈谈怎么提高Android性能问题吧! 也许你经常会遇到比较复杂的布局,这种情况下,最简单的方法就是多层嵌套实现效果,但是最简单的方法是否是最优的方法呢? 这里需要打一个大大的问号?????经验告诉我们,往往简单的方法,得到的结果不是最优解,那么我们通过一个例子来研究一下怎么去优化我们的XML布局吧

Android 卡顿优化 3 布局优化

欲善其事, 先利其器. 分析布局, 就不得不用到Hierarchy Viewer了. 本文工具使用皆以GithubApp的详情界面RepoDetailActivity为例说明. 为了不影响阅读体验, 对应的布局文件activity_repo_detail.xml的代码放在文末 1, Hierarchy Viewer怎么用 Hierarchy发音 [美: 'ha??rɑrki] [英: 'ha??rɑ?k?] 层次结构的意思. 之前一直念不顺这个单词Hierarchy, 就简称为H Viewer了

【Android 开发技巧】布局优化利器&lt;include/&gt;和ViewStub

『原创作品,转载请注明出处. --- 孙国威』 [文章原始地址 http://blog.csdn.net/manoel/article/details/39036507] 当创建复杂的布局的时候,有时候会发现添加了很多的ViewGroup和View.随之而来的问题是View树的层次越来越深,应用也变的越来越慢,因为UI渲染是非常耗时的. 这时候就应该进行布局优化了.这里介绍两种方式,分别为<include>标签和ViewStub类. <include/> 使用<include

Android-性能优化之布局优化

系统在渲染UI界面的时候会消耗大量的资源,一个好的UI不仅仅应该具有良好的视觉效果,更应该具有良好的使用体验,因此布局优化就显得非常的重要. 1.Android UI渲染机制 人眼所感觉到的流畅画面,需要画面的帧数达到40帧每秒到60帧每秒.在Android中,系统通过VSYNC信号触发对UI的渲染和重绘,其间隔时间是16ms.这个16ms其实就是1000ms中显示60帧的画面的单位时间,即1000/60.如果系统每次渲染的时间都保持在16ms之内,那么我们看到的UI界面是非常的流畅,但这也需要