第15章 Android性能优化
为什么要性能优化?
Android设备作为一种移动设备,CPU和内存往往受到一定的限制。
- 过多的使用内存会导致内存溢出,即OOM(Out Of Memory)
- 过多的使用CPU会导致手机出现卡顿甚至出现无法响应的情况,即ANR(Application Not Responding)
一些有效的优化方法
- 布局优化
使用<include>、<merge>和<ViewStub>标签 - 绘制优化
在OnDraw()中一不要创建新的对象,二不要做耗时任务 - 内存泄露优化
养成良好的编程意识比如注意静态变量、单例模式和属性动画造成的内存泄漏 - 相应速度优化
避免ANR,比如不要再主线程里做超过5s的耗时任务,不要再onReceive里面做超过10s的任务,还要注意由于在主线程中等待长时间的同步锁而导致的ANR - ListView优化
在Adapter中利用ViewHolder,在滑动时不要加载数据,开启硬件加速 - Bitmap优化
利用BitmapFactory.Options对象对图片进行压缩 - 线程优化
采用线程池从而避免大量的创建销毁线程,还能控制并发数避免阻塞现象 - 其他优化
- 避免创建过多的对象
- 不要过多使用枚举
- 常量使用static final修饰
- 使用Android特有的数据结构比如SparseArray和Pair等
- 适当使用软引用和弱引用
- 采用内存缓存LreCache和磁盘缓存DiskLruCache
- 尽量采用静态内部类
15.1 Android的性能优化方法
本节介绍了一些有效的性能优化方法。
15.1.1 布局优化
主要思想:布局中的层级少了,这就意味着Android的绘制工作少了,那么程序的性能自然就提高了。
主要方法:使用<include>、<merge>和<ViewStub>标签
<include>标签
<include>标签可以将指定布局加载到当前的布局文件中,通过layout属性来设置指定的布局文件,<include>标签只支持带有android:layout_*这种属性,而且只要设定了相关属性,就必须存在android:layout_width和android:layout_height这两个属性。使用方法如下所示
<include layout="@layout/titlebar"/>
<merge>标签
<merge>标签主要要和<include>标签搭配使用,当<include>标签的最外层布局和所处的当前布局一样时,比如说都是垂直方向的LinearLayout,此时便可以将<include>标签的最外层布局的LinearLayout改成<merge>标签,如下所示
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Content"/>
</merge>
<ViewStub>标签
<ViewStub>标签主要是一个轻量级的按需加载的布局控件,特点是像<include>标签一样包含一个布局文件,还有一个是可以在必要的时候显示和消失,所以ViewStub控件有利于显示一些网络加载或者异常时的界面。使用方法如下。
在布局文件中:
<ViewStub
android:id="@+id/stub"
android:inflatedId="@+id/titlebar_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/titlebar"
/>
在代码中:需要注意的点就是上面inflatedId的理解,它的意思就是包含的根布局id,在上面的代码中就是titlebar.xml这个布局的id,然后通过该id可以获取到titlebar里的控件,比如下面的tvTitle
view = stub.inflate();
View titleBar = findViewById(R.id.titlebar_id);
tvTitle = (TextView) titleBar.findViewById(R.id.title);
btnSwitch.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(view.getVisibility() == View.VISIBLE)
view.setVisibility(View.GONE);
else
view.setVisibility(View.VISIBLE);
}
});
btnChange.setOnClickListener(new OnClickListener() {
int i = 0;
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
tvTitle.setText("i = " + i++);
}
});
15.1.2 绘制优化
绘制优化是指View的onDraw方法要避免大量的操作,主要体现在内存和CPU两个方面:
- onDraw中不要创建新的局部对象
因为onDraw可能会被频繁的调用而导致大量的对象被创建,这会导致占用过多的内存从而频繁地gc; - onDraw中不要进行耗时的操作
频繁的耗时操作会占用CPU的时间大量的时间,导致程序卡顿
15.1.3 内存泄漏优化
内存泄漏:程序在销毁时,还有一些引用没有被释放,导致这些对象占用的内存无法被使用
解决方法:
在开发过程避免写出有内存泄漏的代码,以下是几种容易造成内存泄漏的原因
- 静态变量导致的内存泄漏
- 单例模式导致的内存泄漏
需要在不需要时解除 - 属性动画导致的内存泄漏
所以使用属性动画一定要记得在不可见时取消动画
15.1.4 响应速度优化和ANR分析
响应速度优化的核心思想是避免在主线程做耗时的操作,将耗时的操作放在线程里执行。同时还要注意另一种不是很明显的造成ANR的原因:在主线程中长时间等待同步锁
15.1.5 ListView和Bitmap优化
ListView优化
ListView优化主要发生在Adapter的getView中,14章里有分析,这里总结一下
- 采用ViewHolder进行缓存并且避免在getView中执行耗时操作
- 在滑动时不要加载数据
- 尝试开启硬件加速
Bitmap优化
Bitmap优化主要是通过BitmapFactory的Options对象进行的
方法:利用Options先计算出采样率inSampleSize并返回给Options,最后通过改变的Options对图片进行压缩
15.1.6 线程优化
线程优化主要体现在避免大量的创建和销毁线程,因此线程优化的思想就是使用线程池。使用线程池的好处就是,可以重用线程避免创建和销毁大量线程,还可以控制并发数以避免阻塞
15.1.7 一些性能优化建议
- 避免创建过多的对象
- 不要过多使用枚举
- 常量使用static final修饰
- 使用Android特有的数据结构比如SparseArray和Pair等
- 适当使用软引用和弱引用
- 采用内存缓存LreCache和磁盘缓存DiskLruCache
- 尽量采用静态内部类
15.2 内存泄漏分析之MAT工具
15.3 提高程序的可维护性
本节主要是讲Android的程序设计思想,主旨是如何提高代码的可维护性和可扩展性
代码风格
- 命名要规范
私有成员以m开头,静态成员以s开头,常量则全部用大写字母表示等等 - 代码的排版上需要留出合理的空白来区分不同的代码块
- 仅为非常关键的代码添加注释,其他地方不注释
层次性
代码的层次性是指代码要有分层的概念,不要试图在一个方法或者一个类中去全部实现,而要将它分成几个自逻辑,然后每个子逻辑做自己的事情
扩展性
程序的扩展性要在写程序的过程中时刻考虑到,考虑着如果这个逻辑后面发生了改变那么需要做哪些修改,以及怎么样才能降低修改的工作量
设计模式
恰当地使用设计模式可以提高代码的可维护性和可扩展性,但是Android程序有性能瓶颈,因此要控制设计的度,设计不能太牵强
网友评论