公司产品采取以下措施后,近两个星期,只出现一次的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