0%

Android性能优化之代码优化

  这篇文章主要是介绍了一些小细节的优化技巧,当这些小技巧综合使用起来的时候,对于整个App的性能提升还是有作用的。

虽然选择合适的算法与数据结构可以更好提高App的整体性能,但在这篇文章中不会涉及这方面,有兴趣的可以自己去了解。使用这篇文章中的小技巧作为平时写代码的习惯,这样能够提升代码的效率。

1、内存的合理分配

  Android系统里面有一个Generational Heap Memory的模型,系统会根据内存中不同的内存数据类型分别执行不同的GC操作。例如,最近刚分配的对象会放在Young Generation区域,这个区域的对象通常都是会快速被创建并且很快被销毁回收的,同时这个区域的GC操作速度也是比Old Generation区域的GC操作速度更快的。
GC图
  除了速度差异之外,执行GC操作的时候,所有线程的任何操作都会需要暂停,等待GC操作完成之后,其他操作才能够继续运行。
GC图
  通常来说,单个的GC并不会占用太多时间,但是大量不停的GC操作则会显著占用帧间隔时间(16ms)。如果在帧间隔时间里面做了过多的GC操作,那么自然其他类似计算,渲染等操作的可用时间就变得少了。

导致GC频繁执行有两个原因:
  •Memory Churn内存抖动,内存抖动是因为大量的对象被创建又在短时间内马上被释放。
  •瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,剩余空间不够的时候,也会触发GC。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。这个操作有可能会影响到帧率,并使得用户感知到性能问题。
GC图

  解决上面的问题有简洁直观方法,如果你在Memory Monitor里面查看到短时间发生了多次内存的涨跌,这意味着很有可能发生了内存抖动。
GC图

  同时我们还可以通过Allocation Tracker来查看在短时间内,同一个栈中不断进出的相同对象。这是内存抖动的典型信号之一。
  当你大致定位问题之后,接下去的问题修复也就显得相对直接简单了。例如,你需要避免在for循环里面分配对象占用内存,需要尝试把对象的创建移到循环体之外,自定义View中的onDraw方法也需要引起注意,每次屏幕发生绘制以及动画执行过程中,onDraw方法都会被调用到,避免在onDraw方法里面执行复杂的操作,避免创建对象。对于那些无法避免需要创建对象的情况,我们可以考虑对象池模型,通过对象池来解决频繁创建与销毁的问题,但是这里需要注意结束使用之后,需要手动释放对象池中的对象。

2、不要持有Context的静态引用

内存泄露检查工具
  其他对象中的静态成员变量引用Content会造Content不能正常的回收,造成内存泄露。静态变量的内存是不会被回收,直到应用程序退出才回收。比如下图,当Activity finish后造成Activity不能回收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MemoryActivity extends Activity{

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Reference reference = new Reference(this);
}

static class Reference {
private static Context context;

public Reference(Context context) {
this.context = context;
}
}
}

3、在Activit销毁时应该做的事

1)unregisterReceiver,注销不需要的广播接收。
2)释放和关闭当前界面所有网络资源。(比如我们在使用Volley和Picasso, 在基类onDestroy中释放所有的资源)

1
2
3
4
5
6
7
8
@Override
protected void onDestroy() {
super.onDestroy();
// 根据tag this中断请求
RequestManager.cancelPendingRequests(this);
Picasso.with(this).cancelTag(this);
UploadAndDownloadManager.cancelRequest(this); // 根据tag取消上传下载
}

3)释放定时任务。
4)释放handler资源。非静态内部类隐式地持有一个外部类对象的引用,如普通执行任务不释放资源可能不出现内存泄露,如果是handler定时执行任务一定会造成内存泄露。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class HandlerActivity extends Activity{

private final static int WHAT = 1;
private final static int RATE = 1000 * 10;

private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case WHAT:
//do something
sendEmptyMessageDelayed(WHAT, RATE);
break;
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler.sendEmptyMessage(WHAT);
}

@Override
protected void onDestroy() {
super.onDestroy();
handler.removeMessages(WHAT);
}
}

5)释放其引用Activity的资源。(如果其他对象是一个全局的对象,在Activity finish后该对象不回收也会导致Activity不能回收)

4、其他

1)合理的分配任务调度。
2)合理的使用线程池。(本地数据读取,网络数据读取)
3)网络数据优化。 Volley解析
4) 类的内直接使用成员变量访问,避免使用get、set(速度提高3-7倍)。