大幅减少Android OOM的几个措施

3,178 阅读3分钟
原文链接: ie8384.com

公司产品采取以下措施后,近两个星期,只出现一次的OOM

一、神器LeakCanary,在开发阶段检测内存泄露

LeakCanary是一个检测内存泄露的开源类库。你可以在 debug 包中轻松检测内存泄露,在开发阶段进行及时修复。

简单使用方法如下:
在 build.gradle 中加入引用,不同的编译使用不同的引用:

 dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}

在 Application 中:

public class ExampleApplication extends Application {

@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}

我这边其实release没有引用leakcanary, 在主模块的build.gradle里设置,不同buildtype引用不同的source 路径

android {

sourceSets {
debug {
java.srcDirs = ['src/debug/java']
}
release {
java.srcDirs = ['src/release/java']
}
forTest {
java.srcDirs = ['src/forTest/java']
}
}
}

三个路径下,都有一个SHConfig.java,定义同样的包名和同一个接口

在debug下的SHConfig.java引入leakcanary:

package com.showjoy.shop.config;

public class SHConfig {

public static void debugSetting(Application application) {
com.squareup.leakcanary.LeakCanary.install(application);
}
}

然后在Application里调用即可

SHConfig.debugSetting(this);

二、图片压缩和图片加载

  • 所有存在本地的图片通过tinypng.com/压缩,可以多压缩几遍;
  • 本地图片可以转成webp的,尽量转成webp
  • cdn图片服务器集成压缩功能,包括支持webp,支持尺寸压缩,客户端访问图片的时候根据尺寸要求动态添加后缀,如:

  • 使用强大的图片加载库——Fresco,可以直接使用以及封装好的库SHImageView
    • 当图片不再显示在屏幕上时,它会及时地释放内存和空间占用。
    • 在5.0以下系统,Fresco将图片放到一个特别的内存区域。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。
    • 5.0以上系统,由于安卓系统本身内存管理的优化,所以对于5.0以上的系统Fresco将Bitmap缓存直接放到了javaheap内存中.
      并且,fresco实现了真正的三级缓存:两级的内存缓存+一个磁盘缓存.两个内存缓存为:bitmap缓存 和未解码的图片缓存,这样既可以加快图片的加载速度,又能节省内存的占用.这个两级的内存缓存也是其他图片加载框架所没有做到的.
    • 在app切换到后台时,fresco会自动清理两级的内存缓存,无需手动
    • Fresco 在低端机器上表现一样出色,你再也不用因图片内存占用而思前想后。

三、及时清除缓存

在ActivityLifecycleCallbacks里的onActivityStopped及时清除不用的缓存。 首先缓存管理要统一化,清除规则可以自定义,比如

  • 超过可用醉倒内存的60%就开始清除无用的内存。
  • 退到后台时,清除无用的内存
  • CacheUtil.clear()的实现包括清除图片缓存SHImageView.clearMemoryCaches();
public class ActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
…… ……

@Override
public void onActivityStopped(Activity activity) {

float rateOfUsed = 1.0f * Runtime.getRuntime().totalMemory() / Runtime.getRuntime().maxMemory();
LogUtils.d("totalMemory / maxMemory :" + rateOfUsed);
//内存不足时,清空缓存
if (rateOfUsed > 0.6) {
SHAnalyticManager.onEvent("out_of_memory");
CacheUtil.clear();
}

//退到后台后,清除缓存
if (0 >= activityList.size()) {
LogUtils.d("app background");
CacheUtil.clear();

RxBus.getDefault().post(new BackgroundEvent(true));

}
}

四、代码优化

  • 考虑使用ArrayMap/SpareseArray而不是传统的HashMap等数据结构,Android系统为移动系统设计的容器ArrayMap更加高效,占用内存更少,因为HashMap需要一个额外的实例对象来记录Mapping的操作。而SparesArray高效的避免了key和value的自动装箱,而且避免了装箱后的解箱。
  • 杜绝用ScrollView来实现列表,用ListView/GridView/RecycleView等出现大量重复子组件的视图里对ConvertView的复用
  • 代码中会需要使用到大量的字符串拼接的操作,这种时候有必要考虑使用StringBuilder来替代频繁的“+”
  • 谨慎使用static对象
  • 优化布局层次,减少内存消耗
  • 避免使用枚举类型
  • 合理使用onLowMemory& onTrimMemory回调

参考链接:
LeakCanary 中文使用说明
安卓OOM和Bitmap图片二级缓存机制(一)
Android 性能优化(内存之OOM)
fresco封装库SHImageView
应用内存优化之OnLowMemory&OnTrimMemory