在Android的实际开发中,可能会出现不再使用的对象无法被系统回收的情况,这种情况会导致内存泄漏,甚至内存溢出,导致程序崩溃。
检测方法:使用LeakCanary
优化方案:
1.检查使用多少内存
每个 APP 的堆(heap)内存大小有硬性限制,如果您的 APP 已达到堆内存限制,并尝试分配更多的内存,系统会抛出 OutOfMemoryError 。为了避免 OOM ,您可以查询当前设备有多少堆空间,可以通过调用系统 getMemoryInfo() 查询,返回 一个ActivityManager.MemoryInfo 对象,它提供该设备当前存储器的状态信息,包括可用的存储器,总存储器,和低于该阈值存储器。
private void getMemoryInfo() {
ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
LogUtil.d("totalMem=" + memoryInfo.totalMem + ",availMem=" + memoryInfo.availMem);
if (!memoryInfo.lowMemory) {
// 运行在低内存环境
}
}
2.当界面不可见时释放内存
实现 ComponentCallbacks2 API 中 onTrimMemory()) ,当回调参数 level 为 TRIM_MEMORY_UI_HIDDEN ,是用户点击了Home键或者Back键退出应用,所有UI界面被隐藏,这时候应该释放一些不可见的时候非必须的资源。
public class MainActivity extends AppCompatActivity
implements ComponentCallbacks2 {
// Other activity code ...
/**
* Release memory when the UI becomes hidden or when system resources become low.
* @param level the memory-related event that was raised.
*/
public void onTrimMemory(int level) {
// Determine which lifecycle or system event was raised.
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
/*
Release any UI objects that currently hold memory.
The user interface has moved to the background.
*/
break;
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
/*
Release any memory that your app doesn't need to run.
The device is running low on memory while the app is running.
The event raised indicates the severity of the memory-related event.
If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
begin killing background processes.
*/
break;
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
/*
Release as much memory as the process can.
The app is on the LRU list and the system is running low on memory.
The event raised indicates where the app sits within the LRU list.
If the event is TRIM_MEMORY_COMPLETE, the process will be one of
the first to be terminated.
*/
break;
default:
/*
Release any non-critical data structures.
The app received an unrecognized memory level value
from the system. Treat this as a generic low-memory message.
*/
break;
}
}
}
该 onTrimMemory() 回调是在搭载Android 4.0(API级别14)加入。对于早期版本,您可以使用 onLowMemory() 回调作为旧版本的回调,这大致相当于 TRIM_MEMORY_COMPLETE事件。
3.谨慎使用服务
离开了 APP 还在运行服务是最糟糕的内存管理错误之一,当 APP 处在后台,我们应该停止服务,除非它需要运行的任务。我们可以使用 JobScheduler 替代实现,JobScheduler 把一些不是特别紧急的任务放到更合适的时机批量处理。如果必须使用一个服务,最佳方法是使用 IntentService ,限制服务寿命,所有请求处理完成后,IntentService 会自动停止。
4.使用优化的数据容器
考虑使用优化过数据的容器 SparseArray / SparseBooleanArray / LongSparseArray 代替 HashMap 等传统数据结构,通用 HashMap 的实现可以说是相当低效的内存,因为它需要为每个映射一个单独的条目对象。
5.避免在Android上使用枚举
6.使用 nano protobufs 序列化数据
Protocol buffers 是一个语言中立,平台中立的,可扩展的机制,由谷歌进行序列化结构化数据,类似于 XML 设计的,但是更小,更快,更简单。如果需要为您的数据序列化与协议化,建议使用 nano protobufs。
7.避免内存流失
8.使用ProGuard来剔除不需要的代码
使用 ProGuard 来剔除不需要的代码,移除任何冗余的,不必要的,或臃肿的组件,资源或库完善 APP 的内存消耗。
9.减少apk体积
您可以通过减少 APP 的整体规模显著减少 APP 的内存使用情况。文章:Android APK瘦身实践
10.优化布局层次
通过优化视图层次结构,以减少重叠的 UI 对象的数量来提高性能。文章:Android 渲染优化
11.使用 Dagger 2依赖注入
依赖注入框架可以简化您写的代码,并提供一个自适应环境测试和便于其他配置的更改。如果打算在您的 APP 使用依赖注入框架,可以考虑用 Dagger 2 ,Dagger 不使用反射扫描 APP 的代码,Dagger 是静态的,意味着它编译时不需要占用运行 Android 应用或内存的使用。
12.小心使用外部库
13.避免Bitmap浪费
Bitmap是内存消耗的大头,当使用时要及时回收。另外配置:
inSampleSize:缩放比例,图片按需加载,避免不必要的大图载入。
decode format:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差异。
14.Cursor关闭
如查询数据库的操作,使用到Cursor,也要对Cursor对象及时关闭。
15.监听器的注销
Android程序里面存在很多需要register与unregister的监听器,手动add的listener,需要记得及时remove这个listener。