Android中,如何提升Layout的性能?
Layout 是 Android 应用中直接影响用户体验的关键部分。如果实现的不好,你的 Layout 会导致程序非常占用内存并且 UI 运行缓慢。Android SDK 带有帮助你找到 Layout 性能问题的工具。
主题一:优化Layout层级
一个常见的误区是,用最基础的Layout结构可以提高Layout的性能。然而,因为程序的每个组件和Layout都需要经过初始化、布局和绘制的过程,如果布局嵌套导致层级过深,上面的初始化、布局和绘制操作就更加耗时。例如,使用嵌套的LinearLayout可能会使得View的层级结构过深,此外嵌套使用了 layout_weight参数的LinearLayout的计算量会尤其大,因为每个子元素都需要被测量两次。这对需要多次重复inflate的Layout尤其需要注意,比如嵌套在 ListView或GridView时。
In this lesson you'll learn to use Hierarchy Viewer and Layoutopt to examine and optimize your layout. 使用两个工具:Hierarchy Viewer和Layoutopt。
如何审视自己设计的Layout?
Android SDK 工具箱中有一个叫做 Hierarchy Viewer 的工具,能够在程序运行时分析 Layout。你可以用这个工具找到 Layout 的性能瓶颈。
Hierarchy Viewer 会让你选择设备或者模拟器上正在运行的进程,然后显示其 Layout 的树型结构。每个块上的交通灯分别代表了它在测量、布局和绘画时的性能,帮你找出瓶颈部分。比如,下图是 ListView 中一个列表项的Layout。列表项里,左边放一个小位图,右边是两个层叠的文字。像这种需要被多次 inflate 的 Layout ,优化它们会有事半功倍的效果。
The hierarchyviewer tool is available in <sdk>/tools/. Click Load View Hierarchy to view the layout hierarchy of the selected component.
找到UI性能瓶颈了,如何修正Layout?
上面的 Layout 由于有这个嵌套的 LinearLayout 导致性能太慢,可能的解决办法是将 Layout 层级扁平化 - 变浅变宽,而不是又窄又深。RelativeaLayout 作为根节点时就可以达到目的。所以,当换成基于 RelativeLayout 的设计时,你的 Layout 变成了两层。新的Layout变成如下形式:
可能看起来是很小的进步,但是由于它对列表中每个项都有效,这个时间要翻倍。这个时间的主要差异是由于在 LinearLayout 中使用 layout_weight 所致,因为会减慢“测量”的速度。这只是一个正确使用各种 Layout 的例子,当你使用 layout_weight 时有必要慎重。
如何使用Lint工具辅助检测?
运行 Lint 工具来检查 Layout 可能的优化方法,是个很好的实践。Lint 已经取代了Layoutopt工具,它拥有更强大的功能。
从Eclipse中如何启动Lint?如下方式均可以:
Lint中包含如下检测规则:
使用compound drawable — 用一个compound drawable 替代一个包含 ImageView 和 TextView 的 LinearLayout 会更有效率。
合并根 frame — 如果 FrameLayout 是 Layout 的根节点,并且没有使用 padding 或者背景等,那么用 merge 标签替代他们会稍微高效些。
没用的子节点 — 一个没有子节点或者背景的 Layout 应该被去掉,来获得更扁平的层级。
没用的父节点 — 一个节点如果没有兄弟节点,并且它不是 ScrollView 或根节点,没有背景,这样的节点应该直接被子节点取代,来获得更扁平的层级。
太深的 Layout — Layout 的嵌套层数太深对性能有很大影响。尝试使用更扁平的 Layout ,比如 RelativeLayout 或 GridLayout 来提高性能。一般最多不超过10层。
主题二:使用<include>标签重复利用布局
虽然 Android 提供很多小的可重用的交互组件,你仍然可能需要重用复杂一点的组件,这也许会用到 Layout。为了高效重用整个的 Layout,你可以使用 <include/> 和 <merge/> 标签把其他 Layout 嵌入当前 Layout。
重用 Layout 非常强大,它让你可以创建复杂的可重用 Layout。比如,一个 yes/no 按钮面板,或者带有文字的自定义进度条。这也意味着,任何在多个 Layout 中重复出现的元素可以被提取出来,被单独管理,再添加到 Layout 中。所以,虽然可以添加一个自定义 View 来实现单独的 UI 组件,你可以更简单的直接重用某个 Layout 文件。
如何创建可重用的Layout?
如果你已经知道你需要重用的 Layout,就先创建一个新的 XML 文件并定义 Layout 。比如,以下是一个来自 G-Kenya codelab 的 Layout,定义了一个需要添加到每个 Activity 中的标题栏(titlebar.xml):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width=”match_parent”
android:layout_height="wrap_content"
android:background="@color/titlebar_bg">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gafricalogo" />
</FrameLayout>
其中:根节点的View类型就是你想要的添加进入的Layout。
如何添加到指定Layout中,使用<include>标签
使用 <include> 标签,可以在 Layout 中添加可重用的组件。比如,这里有一个来自 G-Kenya codelab 的 Layout 需要包含上面的那个标题栏:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:background="@color/app_bg"
android:gravity="center_horizontal">
<include layout="@layout/titlebar"/>
<TextView android:layout_width=”match_parent”
android:layout_height="wrap_content"
android:text="@string/hello"
android:padding="10dp" />
...
</LinearLayout>
你也可以覆写被添加的Layout的所有Layout参数(任何android:layout_* 属性),通过在<include/>中声明他们来完成。比如:
<include android:id=”@+id/news_title”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
layout=”@layout/title”/>
但必须指出的是:如果想要覆写被加入Layout的属性,必须先覆写其layout_width和layout_height属性。
另一个方式:使用<merge>标签
<merge /> 标签在你嵌套 Layout 时取消了 UI 层级中冗余的 ViewGroup 。比如,如果你有一个 Layout 是一个竖直方向的 LinearLayout,其中包含两个连续的 View 可以在别的 Layout 中重用,那么你会做一个 LinearLayout 来包含这两个 View ,以便重用。不过,当使用一个 LinearLayout 作为另一个 LinearLayout 的根节点时,这种嵌套 LinearLayout 的方式除了减慢你的 UI 性能外没有任何意义。
为了避免这种情况,可以使用<merge>元素来替代可重用Layout的根节点。
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/add"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/delete"/>
</merge>
现在,当你要将这个 Layout 包含到另一个 Layout 中时(并且使用了 <include/> 标签),系统会忽略 <merge> 标签,直接把两个 Button 放到 Layout 中 <include> 的所在位置。
主题三:按需加载布局
主题四:优化ListView滑动性能
保持程序流畅的关键,是让主线程(UI 线程)不要进行大量运算。你要确保在其他线程执行磁盘读写、网络读写或是 SQL 操作等。为了测试你的应用的状态,你可以启用 StrictMode。
使用后台线程加载数据
Using a background thread ("worker thread") removes strain from the main thread so it can focus on drawing the UI.In many cases, using AsyncTask provides a simple way to perform your work outside the main thread. UI线程仅仅做Layout的绘制,“worker thread”运行后台任务。
// Using an AsyncTask to load the slow images in a background thread
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
@Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
return mFakeImageLoader.getImage();
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.position == position) {
// If this item hasn't been recycled already, hide the
// progress and set and show the image
v.progress.setVisibility(View.GONE);
v.icon.setVisibility(View.VISIBLE);
v.icon.setImageBitmap(result);
}
}
}.execute(holder);
这个行为是全局的,这意味着你不需要考虑自己定义线程池的事情。从 Android 3.0 (API level 11) 开始, AsyncTask 有个新特性,那就是它可以在多个 CPU 核上运行。你可以调用 executeOnExecutor()而不是execute(),前者可以根据CPU的核心数来触发多个任务同时进行。
使用ViewHolder填入视图对象
你的代码可能在 ListView 滑动时经常使用 findViewById(),这样会降低性能。即使是 Adapter 返回一个用于回收的 inflate 后的视图,你仍然需要查看这个元素并更新它。避免频繁调用 findViewById() 的方法之一,就是使用 ViewHolder(视图占位符)的设计模式。
一个 ViewHolder 对象存储了他的标签下的每个视图。这样你不用频繁查找这个元素。第一,你需要创建一个类来存储你会用到的视图。比如:
static class ViewHolder {
TextView text;
TextView timestamp;
ImageView icon;
ProgressBar progress;
int position;
}
在Layout类中生成一个ViewHolder实例:
ViewHolder holder = new ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.listitem_image);
holder.text = (TextView) convertView.findViewById(R.id.listitem_text);
holder.timestamp = (TextView) convertView.findViewById(R.id.listitem_timestamp);
holder.progress = (ProgressBar) convertView.findViewById(R.id.progress_spinner);
convertView.setTag(holder);
这样你就可以轻松获取每个视图,而不是使用 findViewById() 来不断查找子视图,节省了宝贵的运算时间。
Android中,如何提升Layout的性能?的更多相关文章
- Android中View的layout mechanism(布局机制)
layout mechanism Android中View的layout mechanism主要分为两个阶段:measure阶段和layout阶段.layout mechanism按照一定的顺序进行, ...
- 关于Android中ArrayMap/SparseArray比HashMap性能好的深入研究
由于网上有朋友对于这个问题已经有了很详细的研究,所以我就不班门弄斧了: 转载于:http://android-performance.com/android/2014/02/10/android-sp ...
- android中监听layout布局
android开发可以对layout文件夹中的xml文件里的布局进行监听,并处理事件,如:对RelativeLayout,LinearLayout,FrameLayout,GridLayout等布局容 ...
- 源代码解析Android中View的layout布局过程
Android中的Veiw从内存中到呈如今UI界面上须要依次经历三个阶段:量算 -> 布局 -> 画图,关于View的量算.布局.画图的整体机制可參见博文 < Android中Vie ...
- Android中查找一个Layout中指定的子控件
我们通常希望查找一个页面中指定类型的控件,单个控件知道id很容易找到,但是如果是多个呢?或者说是在程序中自定义的控件,且不知道id怎么办呢?如想找到页面中的Spinner,可用以下方法 /** * 从 ...
- 源码解析Android中View的measure量算过程
Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文< Android中View ...
- 使用OPCache提升PHP的性能
对于 PHP 这样的解释型语言来说,每次的运行都会将所有的代码进行一次加载解析,这样一方面的好处是代码随时都可以进行热更新修改,因为我们不需要编译.但是这也会带来一个问题,那就是无法承载过大的访问量. ...
- 那些Android中的性能优化
性能优化是一个大的范畴,如果有人问你在Android中如何做性能优化的,也许都不知道从哪开始说起. 首先要明白的是,为什么我们的App需要优化,最显而易见的时刻:用户say,什么狗屎,刷这么久都没反应 ...
- 在Android中使用并发来提高速度和性能
Android框架提供了很实用的异步处理类.然而它们中的大多数在一个单一的后台线程中排队.当你需要多个线程时你是怎么做的? 众所周知,UI更新发生在UI线程(也称为主线程).在主线程中的任何操作都会阻 ...
随机推荐
- 访问和获取Cookie
Cookie[] cookies = request.getCookies();//从会话里取出Cookie并放在Cookie数组里 if(cookies!=null) { for(Cookie c: ...
- Git的常见基础操作命令
Git的常见基础操作命令 1安装初始化 1.1安装git本地安装Windows版本 下载地址: https://git-scm.com/downloads/ 1.2初始化Git用户信息配置 配置git ...
- EntityFramework Reverse POCO Code First 反向生成器
https://marketplace.visualstudio.com/items?itemName=SimonHughes.EntityFrameworkReversePOCOGenerator ...
- 字符转ASCII码
char k = '成'; int str = (int)k; Console.WriteLine(str); 结果25104就是‘成’对应的ASCII值
- SQL Server 对字符进行排序(数字类的字符)
使用的是SQLserver数据库测试,其他数据库类似. 默认排序是按ID进行排序的 select stu_id ,stu_name from student order by stu_id. 现在需要 ...
- CAS5.3.X 配置备忘
## # 普通MD5用户jdbc验证 ## #配置数据库连接 cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver cas.auth ...
- pp 总结一
1.JQ $.get() <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...
- nginx 源码安装的重启命令
源码安装nginx就面临这样的麻烦,不能使用service nginx restart 来重启nginx,没办法只能重新加载下nginx. #/usr/local/nginx/sbin/nginx - ...
- zabbix_agent YUM源配置
wget http://repo.zabbix.com/zabbix/3.0/rhel/5/x86_64/zabbix-release-3.0-1.el5.noarch.rpm rpm -ivh za ...
- laravel 错误 1071 Specified key was too long; max key length is 1000 bytes
laravel 执行 php artisan migrate 安装数据库报 1071 Specified key was too long; max key length is 1000 bytes ...