最近新做的项目,有点性能问题,今天集中弄了下性能优化,又发现了一个 kotlin 中的坑。其实也不算是坑,只是使用不熟练而已。
介绍下场景:项目有用到高德地图,高德地图的 MapView 需要在 Activity 或者 Fragment 的声明周期中进行调用,就像这样:
1 |
|
然后在我写的某个 Fragment 中用到了 MapView,是用 kotlin 写的。发现每次打开这个页面,内存占用只增不减,在排除了其他因素之后,将问题定位到了高德地图的回收上。
1 | override fun onDestroy() { |
这是 Fragment 里的 onDestroy 调用,断点到这里,发现 downloadMap 总是为 null。这是为啥呢?这个 downloadMap 就是定义到 xml 中的 MapView 呀。
1 | <com.amap.api.maps.MapView |
后面网上查阅资料,发现问题的本质:**kotlin 在 Fragment 中使用 xml 中 id 直接使用控件,也是使用了 findViewById ,同时进行缓存。释放缓存的操作在 onDestroyView 里,所以在 onDestroy 里,使用 xml id 获取的控件已经全部被置空了,所以无法使用。 **
AndroidStudio 中可以直接打开 kotlin Bytecode,进行反编译查看编译后的代码,Tools -> Kotlin -> Show Kotlin Bytecode -> Decompile,看到编译后的代码:
1 | public void onDestroy() { |
果不其然,所以在 onDestroy 中我执行 MapView 的 onDestroy 时 MapView 已经为 null 了,从而导致内存一直占用,无法释放。
那么在 Activity 中是否存在这样的问题呢?答案是不存在。Activity 中也会生成 _$_clearFindViewByIdCache
方法,但是没有地方调用。
结论:在利用 Kotlin 编写的 Fragemnt 中,如果有些特殊的 View 需要处理销毁事件,需要写在 onDestroyView 中,并且在 super 调用之前。这里的 View 必须是直接通过 kotlin 利用 id 从 xml 获取的 View,如果是自己手动写 findViewById 的方式则不会出现这个问题。若是在 Activity 中则没有限制。