android设备作为一种移动设备,不管是内存还是CPU的性能都收到了一定的限制,无法做到像PC设备那样具有超大的内存和高性能的CPU。鉴于这一点,这也意味着android程序不可能无限制的使用内存和CPU资源,过多的使用内存会导致程序内存溢出,即OOM。而过多的使用CPU资源,一般是指做出大量的耗时任务,会导致手机变的卡顿无法响应的情况,即ANR。
-
布局优化
布局优化的思想其实很简单,就是尽量减少布局文件的层级。
- 进行布局优化,首先删除布局中无用的控件和层级,其次有选择的使用性能较低的viewGroup,比如RelativeLayout。如果布局中既可以使用LinearLayout也可以使用RelativeLayout,那么就采用LinearLayout,这是因为RelativeLayout功能比较复杂,它的布局过程需要花费更多的CPU。
- 布局优化的另一种手段就是采用 < include>标签、< merge>标签和ViewStub.
< include> 标签可以将一个指定的布局文件加载到当地的文件中。需要注意的是,如果在布局文件中的根元素中设置了ID,而在< include>也设置了id,以标签中的属性为准。
< merge>标签一般和< include>标签一起使用从而减少布局的层级。如果当前布局文件中存在一个LinearLayout,而被包含的布局文件中也使用了LinearLayout,那么显然被包含的是多余的,通过< merge>标签可以去掉多余的这一层LinearLayout。
ViewStub继承了View,它非常轻量级而且宽、高都是0,因此它本身不参与任何的布局绘制过程。ViewStub的意义在于按需加载所需的布局文件,在实际开发中,很多布局文件不会在正常情况不会显示,比如网络异常的界面,这个时候就没必要在初始化的时候,就把该界面加载进来,通过ViewStub就可以做到在使用的时候再加载,提高了程序初始化的性能。
<ViewStub
android:id="@+id/stub_import"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/layout_network_error"
/>
以下面两种方式进行:
findViewById(R.id.stub_impout).setVisibility(View.VISIBLE);
当ViewStub通过setVisibility方法加载后,ViewStub就会被它内部的布局替换掉,这个时候ViewStub就不再是整个布局结构中的一部分了。另外,目前ViewStub还不支持< merge>标签。
- 绘制优化
绘制优化是指View的onDraw方法要避免执行大量的操作,这主要体现在两个方面。
- 首先,onDraw中不要创建新的局部现象,这是因为onDraw方法可能会被频繁调用,这样就会在一瞬间产生大量的临时对象,这不仅占用了过多的内存而且还会导致系统更加频繁GC,降低了程序的执行效率。
- onDraw方法中不要做耗时的任务,也不能执行成千上万的循环操作,尽管每次循环都很轻量级,但是大量的循环仍然十分抢占CPU的时间片,这会照成View的绘制的不流畅。按照Google官方给出的性能优化典范中的标准,View的绘制帧率保证60fps都是最佳的,这就要求每帧的绘制时间不超过16ms(16ms = 1000/60),虽然程序很难保证16ms这个时间,但是尽量降低onDraw方法的复杂度总是切实有效的。
- 内存泄露优化
- 内存泄漏
3.1 静态变量导致的内存泄漏
下面代码引用了静态mContext,导致Activity不能正常销毁,导致内存泄漏。
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private static Context context;
@Override
protected void onCreate(Bundle saveInstanceState){
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
context = this;
}
}
3.2 单例模式导致的内存泄漏
静态变量导致的内存泄漏都比较明显,相应的单例模式带来的内存泄漏是容易让我们忽视的。
示例代码如下:
public class SingleInstance {
private Context mContext;
private static SingleInstance instance;
private SingleInstance(Context context) {
this.mContext = context;
}
public static SingleInstance getInstance(Context context) {
if (instance == null) {
instance = new SingleInstance(context);
}
return instance;
}
public void say() {
Log.i("tag", "this is single instance");
Log.i("tag", ":code:" + instance.hashCode());
}
很明显的原因在于引用的context对象,当在程序中被activity调用了,恰巧activity先被销毁的情况下,因为一直在被static所修饰的类引用,会造成内存中不能被销毁,导致内存泄漏。
解决办法是可以将应用的ApplicationContext赋值给SingleInstance 的Context
public static SingleInstance getInstance(Context context) {
if (instance == null) {
instance = new SingleInstance(context.getApplicationContext());
}
return instance;
}
3.3 属性动画导致的内存泄漏
从android3.0开始,Google提供了属性动画,属性动画中有一类无限循环的动画,如果在activity播放此类动画且没有在onDestory中去停止动画,那么动画会一直播放下去,尽管已经无法在界面上看到动画效果了,并且这个时候Activity的View会被动画持有,而View又持有了Activity,最终Activity无法释放。
4. 响应速度优化
响应速度优化的核心思想是避免主线程中耗时操作。android中规定,activity如果5秒钟10秒钟之内还未执行完操作也会出现ANR。
5. 线程优化
线程优化的思想就是线程池,避免程序中存在大量的Tread。线程池可以重用内部的线程,从而避免的线程的创建和销毁带来的性能开销,同时线程池还能有效的控制线程池的最大并发数,避免大量的线程因相互抢占资源从而导致阻赛现象的对象。
6. 电池电量优化