在Android开发中,内存优化是保持应用性能良好和避免内存泄漏的一个重要方面。以下是一些常用的内存优化方法和最佳实践:
-
避免内存泄漏:
- 确保你在不需要时,及时注册和注销广播接收器、事件总线(如 EventBus)。
- 避免在匿名类、内部类、线程或异步任务中持有Activity、Context或View的长生命周期的引用。
- 使用
WeakReference
或SoftReference
来引用需要长时间存储但又不妨碍它们被垃圾收集器回收的对象。
-
高效使用集合:
- 根据具体需要选择合适大小和类型的集合类(如使用
SparseArray
系列代替HashMap
)以减少内存开销。 - 对于容器类,如List或Map,预先分配正确的容量可以避免重新分配和复制时的额外开销。
- 根据具体需要选择合适大小和类型的集合类(如使用
-
使用Bitmaps时的优化:
- 使用适当的图像大小和分辨率。不要为小的ImageView加载高分辨率的图片。
- 考虑在不影响用户体验的情况下对图片进行压缩。
- 复用Bitmap的内存,尽可能使用
BitmapFactory.Options.inBitmap
。
-
垃圾收集优化:
- 减少创建短生命周期的对象,例如在循环中创建对象,这些都会增加垃圾回收的频率,影响性能。
- 使用基本类型(如int, float)替代装箱类型(如Integer, Float),减少不必要的自动装箱操作。
-
内存监测工具:
- 使用Android Studio的内存监测工具Profile GPU Rendering、Memory Profiler和Allocation Tracker来监控内存使用和寻找潜在的内存问题。
- LeakCanary库可以用来检测应用中的内存泄漏。
-
优化布局:
- 使用更加轻量级的View和布局,避免嵌套过深的布局,可以使用
ConstraintLayout
来减少布局的复杂性。 - 使用
ViewStub
或RecyclerView
的延迟加载来加载UI,以减轻内存的即时开销。
- 使用更加轻量级的View和布局,避免嵌套过深的布局,可以使用
-
谨慎使用单例和静态集合:
- 单例和静态集合如果不正确使用,可能会造成内存泄漏。确保它们不会意外地保持对Context或其他大对象的引用。
-
使用Android系统提供的组件和模式:
- 当应用进入后台时,利用
onTrimMemory()
回调来释放不需要的资源。 - 使用
ViewModel
来存储和管理与UI相关的数据,避免在Activity/Fragment重建时丢失数据,而降低性能。
- 当应用进入后台时,利用
-
避免在主线程完成复杂和耗时的操作:
- 使用异步处理任务,例如
AsyncTask
、RxJava
、协程来避免在主线程中进行大量的计算,这样可以减少因为暂停垃圾回收造成的卡顿。
- 使用异步处理任务,例如
-
减少资源消耗:
- 避免使用不需要的资源,例如信号量、服务等,确保在不使用它们时将它们关闭或者注销。
具体示例
1. Handler内存泄露的原因是什么?
2. EventBus取消注册
// 取消注册
EventBus.getDefault().unregister(this);
3. 管理RxJava的生命周期
在使用RxJava的时候,如果没有及时解除订阅,在退出Activity的时候,异步线程还在执行,对Activity的引用还在,此时就会产生内存泄露问题。
可使用RxLifecycle,传送门:https://github.com/trello/RxLifecycle
引入依赖
implementation 'com.trello.rxlifecycle4:rxlifecycle:4.0.2'
implementation 'com.trello.rxlifecycle4:rxlifecycle-components:4.0.2'
让你的Activity继承RxAppCompatActivity,Fragment继承RxFragment,其余类似,然后使用bindUntilEvent或者bindToLifecycle
Observable.interval(1000, TimeUnit.MILLISECONDS)
.compose(bindUntilEvent(ActivityEvent.DESTROY)) //当前Activity执行到onDestroy时,Observable取消订阅
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Throwable {
Log.i(tag, "accept:" + aLong);
}
});
Observable.interval(1000, TimeUnit.MILLISECONDS)
.compose(bindToLifecycle())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Throwable {
Log.i(tag, "accept:" + aLong);
}
});
使用bindToLifecycle:
如果Observable在onCreate执行,那么当执行到onDestroy时取消订阅。
如果Observable在onStart执行,那么当执行到onStop时取消订阅。
如果Observable在onResume执行,那么当执行到onPause时取消订阅。
-
加载大量图片时
4.1 参考郭霖大神的博客
Android高效加载大图、多图解决方案,有效避免程序OOM
Android照片墙应用实现,再多的图片也不怕崩溃
Android瀑布流照片墙实现,体验不规则排列的美感总结:
关键是用好内存和硬盘缓存
a. 先从内存中读取(使用LruCache方式)
b. 从本地文件读取
c. 再从网络上进行下载,保存到本地文件以及 通过压缩后,保存到内存中4.2 Glide 缓存策略
https://www.jianshu.com/p/2e25eb85b059