Android 卡顿优化 3 布局优化 工具 Hierarchy Viewer
欲善其事, 先利其器. 分析布局, 就不得不用到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的套路如下:
- 手机开启开发者模式, USB调试.
- 根据手机的Android系统版本:
- 4.0及以下, 没有root. 使用上述的开源工程ViewServer提供的方式.
- 4.0及以下, 已经root. 无需其他额外设置.
- 4.1及以上. 需要在PC端设置ANDROID_HVPROTO环境变量.
设置系统环境变量: ANDROID_HVPROTO, 值为ddm
具体设置系统环境变量根据PC系统不同而异.
做完上述配置后, 你就可以打开H Viewer了, 打开DDMS, 如下操作进入H Viewer界面:
![](http://upload-images.jianshu.io/upload_images/851999-f2746cca39aae936.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/431)
1.2 H Viewer界面详解
以GithubApp的详情界面RepoDetailActivity为例说明:
![](http://upload-images.jianshu.io/upload_images/851999-be24c5e2f6862032.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
界面分为四个部分:
Window
显示当前连接的设备和供分析的界面. 可手动选择.Tree View
树状图的形式展示该Activity中的View层级结构. 可以放大缩小, 每个节点代表一个View, 点击可以弹出其属性, 当前值, 并且在LayoutView中会显示其在界面中相应位置.
Tree View是我们主要要分析的视图.Tree Overview
Tree View的概览图. 有一个选择框, 可以拖动选择查看. 选中的部分会在Tree View中显示.Layout View
匹配手机屏幕的视图, 按照View的实际显示位置展示出来的框图.
1.3 H Viewer参数解读
- 通过Tree View可以很直观的看到View的层级.
- 点击Tree View的RepoItemView这个节点:
![](http://upload-images.jianshu.io/upload_images/851999-94fb323cb5a67573.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
关于三个小圆点的性能指示, 在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检查:
![](http://upload-images.jianshu.io/upload_images/851999-e73744d47dd4f7da.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/620)
选择当前文件:
![](http://upload-images.jianshu.io/upload_images/851999-ff3da05af61e034e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
会在下方弹出分析结果:
![](http://upload-images.jianshu.io/upload_images/851999-c585e0bf720ecfb0.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
分析结果包括用法检测(例如版本特有属性), 国际化(字符串是否提取到strings.xml, Rlt支持等), 以及我们今天的主题---性能分析结果.
点开"Android -> Lint -> Performance"项, 可以看到关于布局性能的建议项. 此例中是说ScrollView的父级LinearLayout是不必要的.
3, 怎么优化你的布局
通过以上工具的使用和分析, 也基本能找到布局的一些常见的好与不好的了.
正所谓授之以鱼不如授之以渔. 在此也就不太详细去讲怎么优化了, 几点建议, 大家自行实践吧:)
尽量减少布局层级和复杂度
- 尽量不要嵌套使用RelativeLayout.
- 尽量不要在嵌套的LinearLayout中都使用weight属性.
- Layout的选择, 以尽量减少View树的层级为主.
- 去除不必要的父布局.
- 善用TextView的Drawable减少布局层级
- 如果H Viewer查看层级超过5层, 你就需要考虑优化下布局了~
善用Tag
- <include>
使用include来重用布局. - <merge>
使用<merge>来解决include或自定义组合ViewGroup导致的冗余层级问题. 例如本例中的RepoItemView的布局文件实际可以用一个<merge>标签来减少一级. - <ViewStub>
ListView优化
- contentView复用
- 引入holder来避免重复的findViewById.
- 分页加载
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/8483636.html
Android 卡顿优化 3 布局优化 工具 Hierarchy Viewer相关推荐
- Android卡顿相关原理和排查工具
Android卡顿优化思考 前言 大家在平时使用手机的时候,是否遇到过我的网络明明很好,怎么一个页面半天跳转不过去,或者是,经常看到在玩王者荣耀和刺激战场时,画面都卡成ppt了,完全是ppt游戏.画面 ...
- 深入探索Android卡顿优化(下)
前言 成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~. 在上篇文章中,笔者带领大家学习了卡顿优化分析方法与工具.自动化卡顿检测方案及优化这两块内容. ...
- Android卡顿检测及优化
前言 之前在项目中做过一些Android卡顿以及性能优化的工作,但是一直没时间总结,趁着这段时间把这部分总结一下. 卡顿 在应用开发中如果留意到log的话有时候可能会发下下面的log信息: I/Cho ...
- 深入解析:Android卡顿检测及优化项目实战经验总结,任君白嫖
前言 之前在项目中做过一些Android卡顿以及性能优化的工作,但是一直没时间总结,趁着这段时间把这部分总结一下. GitHub系统教程学习地址:https://github.com/Timdk857 ...
- 深入探索Android卡顿优化
由于卡顿优化这一主题包含的内容太多,为了更详细地进行讲解,因此,笔者将它分为了上.下两篇.本篇,即为<深入探索Android卡顿优化>的上篇. 本篇包含的主要内容如下所示: 卡顿优化分析方 ...
- Android卡顿优化分析
本篇包含的主要内容如下所示: 1.卡顿优化分析方法与工具 2.自动化卡顿检测方案及优化 在某个 App 的时候,有时我们会看到某个 App 运行起来,即出现了卡现象,如何去定义发生了卡现象呢?马上来了 ...
- Android 卡顿优化之 Skipped * frames 掉帧的计算
Android 卡顿优化之 Skipped * frames 掉帧的计算 有时候看日志的时候,可能会在日志中看到类似下文的打印: Skipped 30 frames! The application ...
- Android系统性能优化(69)---含内存优化、布局优化
Android性能优化:含内存优化.布局优化 前言 在 Android开发中,性能优化策略十分重要 因为其决定了应用程序的开发质量:可用性.流畅性.稳定性等,是提高用户留存率的关键 本文全面讲解性能优 ...
- Android卡顿掉帧问题分析之工具篇
Android卡顿掉帧问题分析之原理篇 Android卡顿掉帧问题分析之工具篇 Android卡顿掉帧问题分析之实战篇 Android卡顿掉帧问题分析之原理篇 公众号:Android技术之家Andro ...
- 现在梦三国2服务器不稳定,《梦三国2》拒绝卡顿 从自我电脑优化开始
原标题:<梦三国2>拒绝卡顿 从自我电脑优化开始 游戏卡顿一直是玩竞技游戏时头疼的问题.游戏卡顿也是战力匹配上分时的噩梦.尤其是每天晚上双倍战力时间段,出现卡顿的几率会更高.我们都知道梦三 ...
最新文章
- 一文读懂机器学习中的模型偏差
- 富士康c语言试卷答案,2015富士康笔试题目及答案
- 开发线程安全的Spring Web应用
- 关于STM32驱动DS1302实时时钟的一点思考
- 如何增加儿童产品中的趣味性?
- menu什么意思中文意思_proclaim什么意思
- 1077篇!ICCV2019接收结果公布,你中了吗?(附7篇论文链接,含Oral)
- python 抢购口罩_Python 京东口罩监控+抢购
- 基于 jQuery支持移动触摸设备的Lightbox插件
- 信息安全工程师笔记-网络安全漏洞防护技术原理与应用
- android开机动画多长时间_Android系统开机动画的一生
- vue中el-image的使用??
- Urllib库的基本使用
- js中的~~、Object.entires用法(转)
- mybatis-plus关联查询,一对一、一对多。
- 我爱看的书-2021-4-17
- android app套壳马甲开发,Android应用开发最佳实践:马甲包配置管理
- 少林寺公布武功秘籍 揭开绝技神秘面纱(组图)
- 声表面波传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- uni-app 对接第三方h5
热门文章
- 在ICT求学时最大的痕迹
- Openvino学习之openvino2022.1版安装配置
- 保姆级教程:深度学习环境配置指南!(Windows、Mac、Ubuntu全讲解)
- 什么是大数据开发?大数据开发要学什么?一个Java转行过程和经历
- 解析S2B2C模式的典型特征,应用S2B2C商城助力医疗器械企业快速发展
- python中查找文件当前位置的命令为tell()_Python文件处理之seek(), tell()用法...
- 程序员是青春饭吗?30岁后的发展方向和突破
- 阿姆斯特朗数 python_Python 阿姆斯特朗数
- windows 下连服务器
- 库 01_EOS 普元 EOS Platform 7.6 开发版安装时没装coframe,后续再部署coframe源码的操作步骤