Android作为一种移动设备,内存和CPU资源都是受限的. 过多地使用内存会导致内存溢出(OOM),过多的使用CPU会导致手机卡顿,甚至出现程序无法响应的情况(ANR). 本文介绍一系列优化方法.
1. 布局优化
1.1 尽量减少布局的层级.
布局的层级少了,那么Android绘制时的工作量就少了,自然能后提高性能.
1.2 ViewGroup的选择.
有选择的使用性能较低的ViewGroup如RelativeLayout的功能比较复杂绘制过程消耗更多的CPU资源 , 尽量使用性能较高的ViewGroup如LinearLayout 和FrameLayout.
1.3 <include> 标签
<include> 标签可以将指定的布局加载到当前布局 .
定义一个布局文件,layout_include.xml
文件.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#f00"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:text="测试按钮"
android:textSize="30sp"
android:layout_height="wrap_content"/>
</LinearLayout>
使用 <include>标签引用布局.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/activity_main"
android:background="#0f0"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 使用 include 标签 -->
<include
android:layout_height="200dp"
android:layout_width="200dp"
layout="@layout/layout_include"/>
</LinearLayout>
效果图如下:
1.4 <merge> 标签
<merge> 标签主要和 <include> 标签一起使用,比如在上面的样例中当前布局使用了一个竖直方向的LinearLayout,在被引用的布局中也使用了一个竖直方向的LinearLayout此时内层的LinearLayout就显得多余了.可以使用<merge>标签替代.从而达到减少布局层级的目的.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="wrap_content"
android:text="测试按钮"
android:textSize="30sp"
android:layout_height="wrap_content"/>
</merge>
1.5 ViewStub
ViewStub继承自View并且宽高都是 0,它本身不参与任何布局和绘制过程. 他可以按需添加布局,有些布局一开始是不需要的,因此可以先不加载,在需要时再进行布局加载.
创建layout_viewstub.xml
文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/stub_import"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:background="#00f"
android:layout_width="match_parent"
android:text="View_Stub测试界面"
android:textSize="30sp"
android:layout_height="match_parent"/>
</LinearLayout>
在activity_main.xml
中添加ViewStub节点
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/activity_main"
android:background="#0f0"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 使用 include 标签 -->
<include
android:layout_height="200dp"
android:layout_width="200dp"
layout="@layout/layout_include"/>
<!-- 使用ViewStub -->
<ViewStub
android:layout_width="200dp"
android:id="@+id/panel_import"
android:layout="@layout/layout_viewstub"
android:inflatedId="@+id/stub_import"
android:layout_height="200dp"/>
<Button
android:layout_width="wrap_content"
android:onClick="onclick"
android:text="加载布局"
android:layout_height="wrap_content"/>
</LinearLayout>
修改MainActivity.java
public void onclick(View view) {
// 加载ViewStub方式一
// ((ViewStub)findViewById(R.id.panel_import)).setVisibility(View.VISIBLE);
// 加载ViewStub方式二
((ViewStub)findViewById(R.id.panel_import)).inflate();
}
android:id="@+id/panel_import"
: 设置ViewStub的id.
android:id="@+id/panel_import"
: ViewStub加载的布局的根布局的id.
android:layout="@layout/layout_viewstub"
: 设置加载布局的资源id.
注意 : ViewStub不支持<merge>标签
2. 绘制优化
绘制优化主要是指View的onDraw方法要避免大量操作. 主要体现如下两方面 :
- onDraw中不要创建新的局部对象, 这是应为onDraw方法会被频繁调用,这样会在一瞬间产生大量临时对象.这样会造成过多的内存效果,更多的GC,降低程序的执行效率.
- onDraw方法中不要进行耗时操作,也不能执行大量的循环操作.虽然每次都是轻量级的但是大量循环还是会十分抢占CPU的时间片.造成View的绘制不流畅.按照Google给出的性能优化典范中的标准,View的帧率保证60fps是最好的.这就要求每一帧图像的绘制不要超过 1000 / 60 = 16ms .因此尽量减低onDraw的复杂度有利于提高性能.
3. 内存泄露优化
内存泄露的优化分为两部分.
- 开发过程中避免写内存泄露的代码.
- 通过一些工具如MAT找出潜在的内存泄露,然后解决掉.
3.1 静态变量导致内存泄露
public class MainActivity extends AppCompatActivity {
private static Context sContext;
private static View sView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 设置
sContext = this;
sView = new View(this);
}
}
上面的代码容易造成内存泄露.
3.2 单例模式导致的内存泄露
单例模式造成内存泄露主要是单例对象保存的外部变量的引用没有被移除造成的.比如单例对象有添加监听者的方法但是没有移除监听者对象的方法.
3.3 属性动画导致的内存泄露
Android 3.0 增加了属性动画,这种动画需要在onDestory中取消动画animator.cancel()
否则动画就会无限循环地执行,造成动画对象会持有View,View持有Activity,最终造成Activity无法释放.造成内存泄露.
3.4 响应速度优化和ANR日志分析
Android系统规定:
- Activity如果 5 秒内无法响应屏幕触摸或者键盘输入时间就会出现ANR.
- BroadcastReciever 10秒内没有执行完操作,就会出现ANR.
当系统发生ANR时会在 data/anr
目录下生成traces.txt
文件.通过分析这个文件就可以找到ANR的原因.
我们在onCreate方法中添加如下代码模模拟ANR
while(true);
查看traces.txt部分内容如下:
便可以定位出问题所在了.
4. ListView 和 Bitmap优化.
4.1 ListView 优化主要有三个方面
- 使用ViewHolder,避免在getView中进行耗时操作.
- 根据列表的滑动状态来控制任务的执行频率,比如当列表快速滑动时不适合开启大量异步任务.
- 开启硬件加速来使ListView更加流畅.
4.2 Bitmap优化
Bitmap优化主要是通过加载压缩后的图片来进行优化的.
5. 线程优化
尽量使用线程池代替开启新的线程.
6. 性能优化建议
- 避免创建过多的对象.
- 不要过多的使用枚举,枚举占用的空间比整形大.
- 常量使用 static final 来修饰.
- 使用一些Android 特有的数据结构,SparseArray 和 Pair 他们具有更好的性能.
- 适当使用软引用和弱引用.
- 采用内存缓存和磁盘缓存.
- 尽量使用静态内部类,避免潜在的由于内部类导致的内存泄露.
参考 : <Android开发艺术探索>