本篇文章主要是介绍业务开发过程中,通过案例分析引入避免内存泄漏的几种思维,借助这些思维写出更加安全稳定的代码,希望能给你带来帮助。
避免长生命周期对象持有短生命周期对象
业务开发中碰到过这样的一个场景,通过Application.registerActivityLifecycleCallbacks()方法注入ActivityLifecycleCallbacks监听所有Activity的创建,并在onActivityResumed(Activity)方法中将参数Activity保存到Application中,这样就能够实现在任何地方获取当前正在显示的界面Activity实例,代码如下:
class MainApplication : Application() {
lateinit var mCurrentActivity: Activity
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityResumed(activity: Activity) {
mCurrentActivity = activity
}
})
}
比如根据网络响应结果弹出一个弹窗,不可能在每个界面Activity中都做一个弹窗的响应处理,这个时候就需要利用这个mCurrentActivity进行Dialog的显示。
这样就会产生一个问题,Applciation的生命周期是比Activity生命周期长的,而一个短周期对象被一个长周期对象就有概率产生内存泄漏的风险,所以业务开发中要避免这样做。
注册监听回调同时要注意反注册
当前有这么一个业务场景:有一个网络监听工具类,界面可以选择性的向这个工具类中添加回调监听,从而获取当前的网络状态及变化。
- 定义一个网络监听回调类:
interface NetworkCallback {
/**
* 断网
*/
fun netDown(): Unit
/**
* 连上网络
*/
fun connected(): Unit
/**
* 网络较差
*/
fun connectWorse(): Unit
}
2.定义一个网络监听回调的集合管理类,进行回调监听的添加移除、网络状况的通知分发:
object NetworkMonitor {
private var mCallbacks: CopyOnWriteArrayList<NetworkCallback> = CopyOnWriteArrayList()
//注册监听
fun register(callback: NetworkCallback) {
mCallbacks.add(callback)
}
//有网分发
fun dispatchConnected() {
mCallbacks.forEach {
it.connected()
}
}
}
3.某个界面MainActivity2添加网络回调监听:
class MainActivity2 : AppCompatActivity() {
private lateinit var mBinding: ActivityMain2Binding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = ActivityMain2Binding.inflate(layoutInflater)
setContentView(mBinding.root)
NetworkMonitor.register(object: NetworkCallback {
override fun netDown() {
}
override fun connected() {
}
override fun connectWorse() {
}
})
}
}
这样看起来有毛病吗,有很大的毛病,总所周知:
匿名内部类默认会持有外部类的引用。在当前的这个案例中,通过NetworkMonitor添加的NetworkCallback实现类会默认持有外部类MainActivity2,这样NetworkMonitor这个全局网络管理类就间接持有了MainActivity2的引用,如果没有反注册监听回调,那么MainActivity2就无法正常销毁了,同样也出现了上面的长生命周期对象持有了短生命周期对象的引用,这样就直接引发了内存泄漏。
当前这个案例中该怎么解决这个问题呢,有两种方式:
1.增加一个反注册方法unregister(),当界面销毁时移除该回调对象:
//移除监听
fun unregister(callback: NetworkCallback) {
mCallbacks.remove(callback)
}
这种需要我们每次比如都要在onDestroyed()中手动调用unregister()方法移除监听,忘了就巴比Q了。
2.借助LifecycleEventObserver绑定界面生命周期,实现自动移除:
object NetworkMonitor {
private var mCallbacks: CopyOnWriteArrayList<NetworkCallback> = CopyOnWriteArrayList()
//注册监听
fun register(activity: LifecycleOwner, callback: NetworkCallback) {
mCallbacks.add(callback)
val callbackWrapper = NetworkCallbackWrapper(callback)
activity.lifecycle.addObserver(callbackWrapper)
}
//有网分发
fun dispatchConnected() {
mCallbacks.forEach {
it.connected()
}
}
//网络监听回调包装类
class NetworkCallbackWrapper(private val callback: NetworkCallback): LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
source.lifecycle.removeObserver(this)
mCallbacks.remove(callback)
}
}
}
}
这里的业务代码只是列出了一部分,主要是为了讲解这个案例,其他的情况暂不进行考虑。
可以看到我增加了一个NetworkCallbackWrapper包装类,包装网络监听回调类NetworkCallback,并实现了LifecycleEventObserver监听界面生命周期,通过改造后的register()方法传入LifecycleOwner添加到界面的生命周期监听集合中,这样当监听到界面销毁时,就移除监听,避免内存泄漏。
有没有感觉这种方式非常的眼熟哈,LiveData的源码中对于观察者的处理就是如此,感兴趣的可以去看下LiveData的observe()方法以及观察者包装类LifecycleBoundObserver。
所以我们可以得出结论,设计一个管理类提供注册监听的时候,一定要提高反注册的方法提供给业务方。
总结
上面介绍的这两种案例是业务开发中非常常见的,所以在进行这种场景开发中一定需要谨记这两种思维:
- 避免长生命周期对象持有短生命周期对象;
- 注册监听回调同时要注意反注册;
同时要善用内存泄漏监听库LeakCanary在Debug环境下对应用测试。
为了帮助到大家更好的掌握性能优化相关知识点,这准备了 性能优化知识点汇总和Android 性能监控框架 的学习文档,中间记录了 启动优化、内存优化、UI优化……等知识点,有需要的可以 点击直接参考↓↓↓ 学习!
有需要的可以复制下方链接,传送直达!!!
https://qr21.cn/CaZQLo?BIZ=ECOMMERCE
内功心法不是一天两天就可以修炼出来的,而是需要每天的坚持,技术提升也是如此。所以最好的速成修炼方法就是每天学习一点,日积月累后就会发现自己进步的效果。
原文地址:https://blog.csdn.net/weixin_61845324
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。