注册

Android-ViewBinding的内存泄露

场景

在MainActivity中分别加载两个Fragment处理业务。
首先触发加载SecondFragment:


//MainActivity触发
supportFragmentManager.commit {
add(R.id.contentLayout, FirstFragment())
addToBackStack(null)//点击返回键可以回到FirstFragment
}

//FirstFragment布局中有一个自定义MyButton且有bitmap属性
class MyButton @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyle: Int = 0
) : Button(
context, attrs, defStyle
) {
private val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.a)
}

然后触发加载SecondFragment;


//MainActivity触发
supportFragmentManager.commit {
replace(R.id.contentLayout, SecondFragment())
addToBackStack(null)
}

Android Profile可以发现有内存泄露

1.png


- MyButton中的bitmap无法释放。


为什么认为bitmap无法释放就是内存泄漏呢?

内存泄漏:简单点说,就是该释放的内存无法得到释放,而且内存不能被使用。
从Fragment的生命周期说起,从FirstFragment切换到SecondFragment,前者生命周期从onPause->onStop->onDestoryView,注意这里只走到onDestoryView,并没有onDetach以及onDestory。其实也很好理解,FirstFragment是加入了回退栈,后续是要被恢复,所以保留了Fragment对象,但为了不占用过多的内存,View会被销毁释放资源。
当FirstFragment从回退栈回到前台,会再次触发onCreateView重建View。既然View会重建,那么之前的View就是不需要的,留着也没用,就应该销毁掉。


该释放的View、Bitmap没有被释放,所以就出现了泄漏。


例子比较简单,只是为了说明问题,如果FirstFragment View持有大量占内存的对象,而且SecondFragment的加载需要耗费比较多的内存且存在跳转的其他页面的可能性,那么FirstFragment View的释放就显得很有必要。


补充引用链:FirstFragment-MyButton-Bitmap


onDestoryView官方注释

注意到这句“The next time the fragment needs
* to be displayed, a new view will be created”,当Fragment恢复时,会创建新的view添加到Fragment,也就是重走onCreateView,那么我理解旧的view就应该可以被销毁。


    /**
* Called when the view previously created by {@link #onCreateView} has
* been detached from the fragment. The next time the fragment needs
* to be displayed, a new view will be created. This is called
* after {@link #onStop()} and before {@link #onDestroy()}. It is called
* <em>regardless</em> of whether {@link #onCreateView} returned a
* non-null view. Internally it is called after the view's state has
* been saved but before it has been removed from its parent.
*/
@MainThread
@CallSuper
public void onDestroyView() {
mCalled = true;
}

LeakCanary日志

建议在onDestroyView要释放掉View


LeakCanary: Watching instance of androidx.constraintlayout.widget.ConstraintLayout (com.yang.myapplication.MyFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks)) with key 0f101dfe-5e4e-4448-95cc-f5d08bbdf06e

解决方案

将ViewBinding置空就欧了。其实这也是官方的建议,当你新建项目的时候,就能看到这样的案列。


   override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

总结

当出现Fragment没有被销毁(onDestory没有回调),而view需要被销毁时(onDestoryView),要注意把ViewBinding置空,以免出现内存泄露。


作者:杨小妞566
链接:https://juejin.cn/post/7088212218205962276
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册