V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
kitalphaj
V2EX  ›  Android

原生的 RecyclerView 真的没有 memory leak 么?

  •  
  •   kitalphaj · 2016-07-26 15:07:40 +08:00 · 14144 次点击
    这是一个创建于 3067 天前的主题,其中的信息可能已经有所发展或是发生改变。
    公司项目用了很多 RecyclerView ,然后发现有内存泄漏的情况。找问题的时候顺便看了一下 RecyclerView 的源码( Android Studio 反编译的吧应该是,不保证正确性),发现在注册 setAdapter 的时候用了 Observable Pattern 。

    大概就是 RecyclerView 里面有个 inner class 是 Observer 用来观察 Adapter 的数据变化。因为用的是非静态内部类,这个 Observer 和这个 RecyclerView 就会有互相引用。这个不是问题的关键因为一旦 RecyclerView 没有 Activity 或者 Fragment 引用了就会一起回收。

    问题是,这个 Adapter 里面的 Observable 也是个内部类,而 Observable 会把传进来的 Observer 存起来。这样的话,如果我 Adapter 放在一个 Fragment 里面,然后这个 Fragment 放到 Backstack 里面去的时候 Adapter 本身应该是不会销毁的( onDestoy 没有调用),这样的话 RecyclerView 也不会被回收(尽管 onDestroyView 调用了)。

    我目前是用了 RecyclerView.setAdapter(null)来解决这个问题,我想知道是不是我打开方式不对?
    第 1 条附言  ·  2016-07-27 09:30:26 +08:00

    又研究了一阵,看了一些开源项目的代码还有Android的一些源码,我发现这个问题其实是有手动解决的办法。

    可以先看看这个问题http://stackoverflow.com/questions/26369905/activitys-ondestroy-fragments-ondestroyview-set-null-practices

    对于Activity来说,OnDestory就真Destory了,不会有这个问题。但是对于Fragment来说,平时我们把它放到Backstack里面的时候,只有View是销毁的,非View的部分是会保留的。这就造成了Adapter不会被自动释放,从而导致RecyclerView不被释放。

    解决方法从两个思路入手:

    1. 在OnDestoryView里,不仅把RecyclerView设为null,同时手动把Adapter设为null,这样就强制断开了Fragment对Adapter的引用。这个方法的缺点是你需要在OnCreateView里面重新初始化Adapter(以前我们可以在OnCreate里面初始化)。其实这样对性能影响并不会很大,因为Adapter一般比较轻量。这种思路可以在这里看到(链接过去的是一个比较出名的Android库)。

    2. 在Fragment里面不保留Adapter的引用。也就是说Adapter是个本地变量,直接创建好了马上传给RecyclerView。之后如果要用就通过RecyclerView去拿就好了。这种方法其实和上面那种很像,只是不需要手动管理引用了。同样是因为没有引用,所以每次创建RecyclerView的时候也要重新创建Adapter。这种思路在Android自己实现的PreferenceFragmentCompat里面可以看到。

    以上两种方法都需要重新创建Adapter,但是都能解决内存溢出问题。想到之前看过的一篇文章说Fragment是Android里面最蛋疼的东西。。。有点后悔用 One Activity + Multiple Fragments这种架构了。。。

    第 2 条附言  ·  2016-07-27 09:58:20 +08:00
    嗯,总的来说就是对于一个 Android 新手,我对 Fragment 理解不够导致了这个问题。
    4 条回复    2016-07-27 15:30:13 +08:00
    a0000
        1
    a0000  
       2016-07-26 17:30:21 +08:00
    fragment 总有被销毁的时候吧
    iluhcm
        2
    iluhcm  
       2016-07-27 12:24:28 +08:00
    我是能不用 Fragment 就不用 Fragment 。。
    torchmu
        3
    torchmu  
       2016-07-27 14:24:25 +08:00   ❤️ 1
    单个 Activity 风险太大了, Fragment 主要是适配不同屏幕、共用组件、插件化来使用,其他还是用 Activity 吧。
    kitalphaj
        4
    kitalphaj  
    OP
       2016-07-27 15:30:13 +08:00
    @torchmu 我这叫不入坑不知道坑大。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4343 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 18ms · UTC 04:06 · PVG 12:06 · LAX 20:06 · JFK 23:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.