Android 卡顿优化 3 布局优化

欲善其事, 先利其器. 分析布局, 就不得不用到Hierarchy Viewer了.

本文工具使用皆以GithubApp的详情界面RepoDetailActivity为例说明.

为了不影响阅读体验, 对应的布局文件activity_repo_detail.xml的代码放在文末

1, Hierarchy Viewer怎么用

Hierarchy发音 [美: ‘ha??rɑrki] [英: ‘ha??rɑ?k?] 层次结构的意思.

之前一直念不顺这个单词Hierarchy, 就简称为H Viewer了. 下文就这么简称吧.

官网描述, H Viewer是用来分析调试和优化我们的UI的一个图形化工具. 它会展示当前界面的View层级.

1.1 启用H Viewer

比较早接触Android开发的同学可能知道, H Viewer只能在root过的机器才能使用. 主要是在没有root过的机器中view server这个服务是没有开启的. H Viewer就无法连接到机器获取view层级信息.

正所谓高手在民间, 大家都尝试在未root的机器中启用view server来使用H Viewer. 最具代表性的就是romainguy的ViewServer, 只需集成少量代码到你的Activity, 相当于在手机端开启了view server服务, 建立socket通道与PC端的H Viewer通信.

此工程被Android官网吸收, 作为开启H View的方案之一.

完整开启H Viewer的套路如下:

  1. 手机开启开发者模式, USB调试.
  2. 根据手机的Android系统版本:
    • 4.0及以下, 没有root. 使用上述的开源工程ViewServer提供的方式.
    • 4.0及以下, 已经root. 无需其他额外设置.
    • 4.1及以上. 需要在PC端设置ANDROID_HVPROTO环境变量.

设置系统环境变量: ANDROID_HVPROTO, 值为ddm

具体设置系统环境变量根据PC系统不同而异.

做完上述配置后, 你就可以打开H Viewer了, 打开DDMS, 如下操作进入H Viewer界面:

ddms_open_hviewer

1.2 H Viewer界面详解

GithubApp的详情界面RepoDetailActivity为例说明:

Snip20160902_1.png

界面分为四个部分:

  1. Window

    显示当前连接的设备和供分析的界面. 可手动选择.

  2. Tree View

    树状图的形式展示该Activity中的View层级结构. 可以放大缩小, 每个节点代表一个View, 点击可以弹出其属性, 当前值, 并且在LayoutView中会显示其在界面中相应位置.

    Tree View是我们主要要分析的视图.

  3. Tree Overview

    Tree View的概览图. 有一个选择框, 可以拖动选择查看. 选中的部分会在Tree View中显示.

  4. Layout View

    匹配手机屏幕的视图, 按照View的实际显示位置展示出来的框图.

1.3 H Viewer参数解读

  1. 通过Tree View可以很直观的看到View的层级.
  2. 点击Tree View的RepoItemView这个节点:

14728281715494.jpg

关于三个小圆点的性能指示, 在App优化之性能分析工具一文中有提到, 再强调一遍:

三个小圆点, 依次表示Measure, Layout, Draw, 可以理解为对应View的onMeasure, onLayout, onDraw三个方法.

  • 绿色, 表示该View的此项性能比该View Tree中超过50%的View都要快.
  • 黄色, 表示该View的此项性能比该View Tree中超过50%的View都要慢.
  • 红色, 表示该View的此项性能是View Tree中最慢的.

如果你的界面的Tree View中红点较多, 那就需要注意了. 一般来说:

1, Measure红点, 可能是布局中嵌套RelativeLayout, 或是嵌套LinearLayout都使用了weight属性.

2, Layout红点, 可能是布局层级太深.

3, Draw红点, 可能是自定义View的绘制有问题, 复杂计算等.

由上图, 可以看到我们的RepoItemView的三项指标都不合格, 证明其还有很多优化空间. 层级, 绘制都可以优化.

除了用H Viewer来做代码后分析, Android还提供了Lint, 在我们编写xml布局文件时就即时的给出一些相关提示.

2, Lint tool

打开RepoDetailActivity的布局文件activity_repo_detail.xml, 在Android Studio菜单栏中开启Lint检查:

14728313149102.jpg

选择当前文件:

14728313382536.jpg

会在下方弹出分析结果:

14728314908964.jpg

分析结果包括用法检测(例如版本特有属性), 国际化(字符串是否提取到strings.xml, Rlt支持等), 以及我们今天的主题---性能分析结果.

点开"Android -> Lint -> Performance"项, 可以看到关于布局性能的建议项. 此例中是说ScrollView的父级LinearLayout是不必要的.

3, 怎么优化你的布局

通过以上工具的使用和分析, 也基本能找到布局的一些常见的好与不好的了.

正所谓授之以鱼不如授之以渔. 在此也就不太详细去讲怎么优化了, 几点建议, 大家自行实践吧:)

尽量减少布局层级和复杂度

  1. 尽量不要嵌套使用RelativeLayout.
  2. 尽量不要在嵌套的LinearLayout中都使用weight属性.
  3. Layout的选择, 以尽量减少View树的层级为主.
  4. 去除不必要的父布局.
  5. 善用TextView的Drawable减少布局层级
  6. 如果H Viewer查看层级超过5层, 你就需要考虑优化下布局了~

善用Tag

  1. <include>

    使用include来重用布局.

  2. <merge>

    使用<merge>来解决include或自定义组合ViewGroup导致的冗余层级问题. 例如本例中的RepoItemView的布局文件实际可以用一个<merge>标签来减少一级.

  3. <ViewStub>

ListView优化

  1. contentView复用
  2. 引入holder来避免重复的findViewById.
  3. 分页加载

4, 附示例代码

因github上的源码会持续更新, 特留对应代码在此.

activity_repo_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/root_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/md_white_1000"
    android:orientation="vertical"
    android:padding="@dimen/dimen_10">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        android:scrollbars="none">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <com.anly.githubapp.ui.widget.RepoItemView
                android:id="@+id/repo_item_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/md_grey_300"
                android:elevation="@dimen/dimen_2"/>

            <LinearLayout
                android:id="@+id/contributor_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/dimen_10"
                android:orientation="vertical"
                >

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/dimen_40"
                    android:gravity="center_vertical"
                    android:orientation="horizontal"
                    android:background="@drawable/button_bg"
                    android:paddingLeft="@dimen/dimen_10">

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:gravity="center_vertical"
                        android:text="{oct-organization} Contributors"/>

                    <TextView
                        android:id="@+id/contributors_count"
                        android:layout_width="match_parent"
                        android:layout_height="@dimen/dimen_40"
                        android:gravity="center_vertical"/>

                </LinearLayout>

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/contributor_list"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/dimen_60"
                    android:layout_marginTop="@dimen/dimen_2"
                    />

            </LinearLayout>

            <LinearLayout
                android:id="@+id/fork_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/dimen_10"
                android:orientation="vertical"
                >

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/dimen_40"
                    android:gravity="center_vertical"
                    android:orientation="horizontal"
                    android:background="@drawable/button_bg"
                    android:paddingLeft="@dimen/dimen_10"
                    >

                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:gravity="center_vertical"
                        android:text="{oct-gist_fork} Forks"/>

                    <TextView
                        android:id="@+id/forks_count"
                        android:layout_width="match_parent"
                        android:layout_height="@dimen/dimen_40"
                        android:gravity="center_vertical"/>

                </LinearLayout>

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/fork_list"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/dimen_60"
                    android:layout_marginTop="@dimen/dimen_2"
                    />

            </LinearLayout>

            <LinearLayout
                android:id="@+id/code_layout"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dimen_40"
                android:gravity="center_vertical"
                android:orientation="horizontal"
                android:layout_marginTop="@dimen/dimen_10"
                android:background="@drawable/button_bg"
                android:paddingLeft="@dimen/dimen_10">

                <TextView
                    android:id="@+id/code_label"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/dimen_40"
                    android:gravity="center_vertical"
                    android:text="{oct-file_code} Code"/>

            </LinearLayout>

            <LinearLayout
                android:id="@+id/readme_layout"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dimen_40"
                android:gravity="center_vertical"
                android:orientation="horizontal"
                android:layout_marginTop="@dimen/dimen_10"
                android:background="@drawable/button_bg"
                android:paddingLeft="@dimen/dimen_10">

                <TextView
                    android:id="@+id/readme_label"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/dimen_40"
                    android:gravity="center_vertical"
                    android:text="{oct-info} README"/>

            </LinearLayout>

        </LinearLayout>

    </ScrollView>
</LinearLayout>

com.anly.githubapp.ui.widget.RepoItemView对应的布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="@dimen/dimen_10">

        <TextView
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="left|center_vertical"
            android:maxLines="1"
            android:text="@string/app_name"
            android:textColor="@android:color/black"
            android:textSize="@dimen/text_size_18"/>

        <TextView
            android:id="@+id/desc"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="left|center_vertical"
            android:maxLines="2"
            android:text="@string/app_name"
            android:textColor="@android:color/darker_gray"
            android:textSize="@dimen/text_size_12"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/dimen_5"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <ImageView
                android:id="@+id/image"
                android:layout_width="@dimen/dimen_32"
                android:layout_height="@dimen/dimen_32"
                android:scaleType="centerInside"
                android:src="@mipmap/ic_launcher"
                android:visibility="visible"/>

            <TextView
                android:id="@+id/owner"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="@dimen/dimen_10"
                android:gravity="left|center_vertical"
                android:text="@string/app_name"
                android:textColor="@android:color/black"
                android:textSize="@dimen/text_size_14"/>

        </LinearLayout>

        <View
            android:layout_marginTop="@dimen/dimen_5"
            android:layout_width="match_parent"
            android:layout_height="1px"
            android:background="@color/grey"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/dimen_32"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:paddingTop="@dimen/dimen_10">

            <TextView
                android:id="@+id/update_time"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="left|center_vertical"
                android:text="@string/app_name"
                android:textColor="@android:color/black"
                android:textSize="@dimen/text_size_12"
                />

            <View
                android:layout_width="1px"
                android:layout_height="match_parent"
                android:background="@color/grey"/>

            <LinearLayout
                android:id="@+id/star_view"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:orientation="horizontal">

                <ImageView
                    android:id="@+id/star_icon"
                    android:layout_width="@dimen/dimen_16"
                    android:layout_height="@dimen/dimen_16"
                    android:scaleType="centerInside"
                    android:src="@drawable/ic_star"/>

                <TextView
                    android:id="@+id/star"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:layout_marginLeft="@dimen/dimen_5"
                    android:gravity="center"
                    android:text="@string/app_name"
                    android:textColor="@android:color/black"
                    android:textSize="@dimen/text_size_12"
                    />

            </LinearLayout>

        </LinearLayout>

    </LinearLayout>

    <com.flyco.labelview.LabelView
        android:id="@+id/label_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        app:lv_background_color="@color/md_yellow_500"
        app:lv_gravity="TOP_RIGHT"
        app:lv_text="TEST"
        app:lv_text_size="@dimen/text_size_12"/>
</FrameLayout>

优化不同于做功能, 可能分析的多, 出的成果少~ 比较枯燥, 然而优化也是App发展的必经之路, 欢迎大家分享经验.

原文地址:https://www.cnblogs.com/ldq2016/p/8483587.html

时间: 2024-10-15 22:40:14

Android 卡顿优化 3 布局优化的相关文章

Android卡顿优化:卡顿分析方法

基础知识在具体讲卡顿工具前,你需要了解一些基础知识,它们主要都和 CPU 相关.造成卡顿的原因可能有千百种,不过最终都会反映到CPU 时间上.我们可以把 CPU 时间分为两种:用户时间和系统时间.用户时间就是执行用户态应用程序代码所消耗的时间:系统时间就是执行内核态系统调用所消耗的时间,包括 I/O.锁.中断以及其他系统调用的时间. CPU 性能在开发过程中,我们可以通过下面的方法获得设备的 CPU 信息.// 获取 CPU 核心数cat /sys/devices/system/cpu/poss

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性能优化之布局优化篇

转自: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实习生 &mdash;&mdash; 屏幕适配及布局优化

为什么要进行屏幕适配.对哪些设备进行适配?在近几年的发展当中,安卓设备数量逐渐增长,由于安卓设备的开放性,导致安卓设备的屏幕尺寸大小碎片化极为严重.从[友盟+]2016年手机生态发展报告H1中看截止16年手机分辨率使用情况:Android设备720p和1080p是主流,如果对前5中Android设备分辨率进行适配就能让app在90%的安卓设备上比较美观的兼容. 涉及重要概念及关系 1.硬件属性 ── 屏幕尺寸.屏幕分辨率.屏幕像素密度 [屏幕尺寸]:屏幕对角线长度.单位是英寸,1英寸=2.54厘

Android 卡顿优化 2 渲染优化

1.概述 2015年初google发布了Android性能优化典范,发了16个小视频供大家欣赏,当时我也将其下载,通过微信公众号给大家推送了百度云的下载地址(地址在文末,ps:欢迎大家订阅公众号),那么近期google又在udacity上开了系列类的相关课程.有了上述的参考,那么本性能优化实战教程就有了坚实的基础,本系列将结合实例为大家展示如何去识别.诊断.解决Android应用开发中存在的性能问题.那么首先带来的就是大家最关注的渲染的性能优化(~~渲染就是把东西绘制到屏幕上). ps:本博客所

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