记一次关于Fragment的内存泄漏_madded in fragmentstore 内存泄露-程序员宅基地

技术标签: android  

之前使用单Activity多Fragment架构完成过一个项目,在后期维护时发现,很多Fragment在关闭之后,内存无法被回收,出现了内存泄漏问题。leakcanary显示引用链信息如下:
ganduwei
LoginFragment关闭后仍然被FragmentManangerImpl中的mCreatedMenus所引用,导致LoginFragment无法被释放。

我又使用Android Profiler工具查看了内存中的实例,步骤如下:
1、运行app,打开Android Profiler工具,然后点击MEMORY,会显示如下视图:
ganduwei
2、点击左上角的“Force garbage collection”按钮执行一次垃圾回收,再点击它旁边的“Dump Java heap”按钮便可将堆中的实例全部分析出来:
ganduwei
如上图我把fragment过滤出来,LoginFragment确实还在内存中,但是FragmentManangerImpl中mAdded中已经不存在LoginFragment,再看看mCreatedMenus:
ganduwei
里面包含了LoginFragment。mCreatedMenus是个什么东西呢,为什么会引用着已关闭的页面?它是FragmentManangerImpl中的属性,只能去FragmentManangerImpl中找找答案了。

在FragmentManangerImpl找到了如下一段代码:

    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        if (this.mCurState < 1) {
            return false;
        } else {
            boolean show = false;
            ArrayList<Fragment> newMenus = null;

            int i;
            Fragment f;
            for(i = 0; i < this.mAdded.size(); ++i) {
                f = (Fragment)this.mAdded.get(i);
                if (f != null && f.performCreateOptionsMenu(menu, inflater)) {
                    show = true;
                    if (newMenus == null) {
                        newMenus = new ArrayList();
                    }

                    newMenus.add(f);
                }
            }

            if (this.mCreatedMenus != null) {
                for(i = 0; i < this.mCreatedMenus.size(); ++i) {
                    f = (Fragment)this.mCreatedMenus.get(i);
                    if (newMenus == null || !newMenus.contains(f)) {
                        f.onDestroyOptionsMenu();
                    }
                }
            }

            this.mCreatedMenus = newMenus;
            return show;
        }
    }

mCreatedMenus会在dispatchCreateOptionsMenu方法中被初始化并赋值,而其他地方并没有改变或重置mCreatedMenus中的元素,所以只有dispatchCreateOptionsMenu方法会影响mCreatedMenus。
dispatchCreateOptionsMenu方法是干嘛的呢,其实从方法名就可以猜到它是用来分发创建ActionBar菜单的。的确,从代码中我们就可以看出,它遍历了mAdded中所有的元素,然后调用元素的performCreateOptionsMenu方法。既然与创建菜单有关,那就去LoginFragment看看。

LoginFragment有创建Toolbar,并把Toolbar交给Activity来创建菜单,代码如下:

	setHasOptionsMenu(true);
	((AppCompatActivity) getActivity()).setSupportActionBar(mToolbar);

而LoginFragment上一级Fragment中并没有创建Toolbar菜单。

到这里我们大致就知道原因了,有两个地方:

1、LoginFragment中有创建菜单,而它的上一级Fragment没有创建菜单,这样导致从LoginFragment返回到上一级后,AppCompatActivity中的FragmentManangerImpl没有执行dispatchCreateOptionsMenu方法,所有mCreatedMenus中还是保存了LoginFragment的实例。如果上一级Fragment有创建菜单不会有此问题;

2、((AppCompatActivity) getActivity()).setSupportActionBar(mToolbar)这句代码导致Activity中引用了Fragment的mToolbar,如果Fragment关闭后,没有去掉这个引用就会导致无法释放Fragment。

当然,如果一个Activity中只有一两个Fragment没什么关系,Activity销毁后也就随之销毁了。但是在单Activity项目中,整个App就一个Activity,被关闭的Fragment在整个App生命周期中一直存在,这样就有很大问题了。

找到原因后,分析出解决方法就比较简单了。解决办法有两个:
1、Fragment中的菜单由自己来创建,不交给Activity,代码如下:

        mToolbar.setNavigationIcon(R.drawable.ic_back);
        mToolbar.setNavigationOnClickListener(v -> {
            //TODO
        });
        mToolbar.inflateMenu(R.menu.toolbar_menu);
        mToolbar.setOnMenuItemClickListener(menuItem -> {
            //TODO
            return true;
        });

2、菜单还是交给Activity管理,如果上一级Fragment有创建菜单那不用处理,如果没有需要在上一级Fragment清除掉引用,代码如下:

        ((AppCompatActivity) getActivity()).setSupportActionBar(null);
        ( getActivity()).onCreatePanelMenu(0,null);

onCreatePanelMenu方法会使dispatchCreateOptionsMenu被调用,从而给mCreatedMenus重新赋值。

当然最好是使用第一个方法,每个Fragment中的菜单由自己来管理。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ganduwei/article/details/82844848

智能推荐

Springboot/java/node/python/php基于springboot+vue手机售后管理系统【2024年毕设】-程序员宅基地

文章浏览阅读779次,点赞19次,收藏24次。springboot微信小程序的小疾病问诊服务系统的设计与实现。springboot基于spring的物业管理系统的设计与实现。springboot基于Java的高校学生请假系统。ssm基于Android的购物商场APP设计与实现。springboot基于微信小程序的智慧校园系统。ssm基于Android的英语词典的设计与开发。ssm基于SSM+Vue的学生实践管理平台开发。ssm基于android的企业员工考勤系统。ssm基于web的暗香小店系统的设计与实现。ssm基于Web的高等学校公费医疗管理系统。

css中hover属性的使用技巧_css hover的用法-程序员宅基地

文章浏览阅读2.3w次,点赞15次,收藏63次。hover属性用不同的书写方式,来改变不同关系的元素样式。元素:hover 表示聚焦后改变自己元素:hover 元素 表示聚焦后改变其子元素元素:hover + 元素 表示聚焦后改变其指定的“亲兄弟”(条件是该兄弟元素与其相邻)元素元素:hover ~ 元素 表示聚焦后改变其指定的兄弟元素,两个元素相不相邻都行。示例:.first:hover {color: white;}/* 聚焦我改变自己 */.three:hover .three-son {font-size: 20px._css hover的用法

coursera-斯坦福-机器学习-吴恩达-第8周笔记-无监督学习_pca反向压缩-程序员宅基地

文章浏览阅读6k次,点赞3次,收藏15次。coursera-斯坦福-机器学习-吴恩达-第8周笔记-无监督学习coursera-斯坦福-机器学习-吴恩达-第8周笔记-无监督学习1聚类算法clutering1聚类算法简介2K-means21kmeans的目标函数22随机初始化23选择类别数3考试quiz维数约减 dimensionality reduction1数据压缩2数据可视化3维度约简-主成分分析法PCA1 PCA_pca反向压缩

vim插件安装及常用技巧_bxbx.vim-程序员宅基地

文章浏览阅读5.2k次。一、插件安装Vundle是vim的一个插件管理器, 同时它本身也是vim的一个插件。插件管理器用于方便、快速的安装、删除、Vim更新插件。mkdir -p ~/.vim/bundlegit clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim管理器安装完成后,vim ~/.vimrc命令创建.vimrc文件syntax on" tab宽度和缩进同样设置为4set tabstop=4set softta_bxbx.vim

java.lang.ClassNotFoundException:如何解决-程序员宅基地

文章浏览阅读7.2w次,点赞10次,收藏41次。本文适用于当前面临java.lang.ClassNotFoundException挑战的Java初学者。 它将为您提供此常见Java异常的概述,这是一个示例Java程序,可支持您的学习过程和解决策略。 如果您对与更高级的类加载器相关的问题感兴趣,我建议您复习有关java.lang.NoClassDefFoundError的文章系列,因为这些Java异常密切相关。 java.lang..._java.lang.classnotfoundexception:

串口通信数据帧_一帧数据-程序员宅基地

文章浏览阅读1.2k次,点赞9次,收藏17次。不同的设备间建立连接往往需要通信,而串口通信是十分常用的一种。UART串口通信需要两根线来实现,一根用于串口发送,另外一更用于串口接收。UART串口发送或者接收过程中一帧数据包括1位起始位、8位数据位、1位停止位,为了提高数据的可靠性可以在停止位前加上1位奇偶校验位。串口通信虽然十分简单,但是在不同设备间发送的数据往往不止1个字节,往往需要多个字节组成的数据包。当我们按照数据包发送时我们需要考虑到以及,因此我们可以采用定义数据帧的方式解决上述两个问题。_一帧数据

随便推点

Ubuntu18.04安装教程(很详细)_ubuntu18安装-程序员宅基地

文章浏览阅读6.6w次,点赞128次,收藏962次。Ubuntu18.0详尽版安装教程下载Ubuntu18.04下载VMware Workstation安装虚拟机下载Ubuntu18.04官方网站:http://old-releases.ubuntu.com/releases/18.04.4/?_ga=2.44113060.1243545826.1617173008-2055924693.1608557140下载VMware Workstation这个在网上有很多教程下载,这里我就不写了,我用的版本是14 pro。如下图:安装虚拟机1、打开_ubuntu18安装

Android四大组件之Activity--管理方式_android activityrecord中的activitytype-程序员宅基地

文章浏览阅读1.7k次。1. 概览Activity的管理有静态和动态两层涵义: 静态是指Activity的代码组织结构,即Application中声明的Activity的集合,这些Activity被组织在一个APK中,有特定的包名。 在编写应用程序时,Activity对应到用户界面,它定义了用户界面的布局、交互行为、启动方式等,最重要的,是Activity的生命周期函数。 在应用进程看来,只需要按照Android定义的规范,实现生命周期函数的具体逻辑即可,所有的用户界面都遵循同一个规范。 编写完一个应用程序的所有用户界面_android activityrecord中的activitytype

[LINUX]sed查找不包含某个字符串的行,并进行替换_sed不包含字符串-程序员宅基地

文章浏览阅读5.5k次,点赞3次,收藏7次。sed 查找不包含某个特性 sed -i "/src/!s/xxx/bbb/g" xxx将不包含src的行中的xxx替换为bbb_sed不包含字符串

问题解决:shared_ptr Assertion px != 0 failed 及debug经验分享_typename boost::detail::sp_dereference<t>::type bo-程序员宅基地

文章浏览阅读6.8k次,点赞11次,收藏18次。问题解决:shared_ptr Assertion px != 0 failed及debug经验分享问题详细描述:/usr/include/boost/smart_ptr/shared_ptr.hpp:646: typename boost::detail::sp_dereference::type boost::shared_ptr::operator*() const [with T = pcl::PointCloudpcl::pointxyz; typename boost::detail::sp_typename boost::detail::sp_dereference::type boost::shared_ptr::operat

看不见的“网” ,一文读懂阿里云基础设施网络_阿里云网络基线理解-程序员宅基地

文章浏览阅读553次。编者按:在这个万物智联的时代,无论是在线网络购物,还是网络强国、数字中国建设,都离不开一张“看不见的网”——基础设施网络。2009年,首届双11每秒交易订单创建峰值400;2021年,双11每秒交易订单创建峰值58.3万,12年交易数字量猛增的背后,是阿里云在庞大分布式系统上计算和IO能力的飞跃,更离不开阿里云基础设施底层网络技术的支撑。图|阿里云全球基础设施网络系统作为阿里云基础设施的重要组成部分,阿里云基础设施网络团队负责整个阿里云全球基础设施网络,包括大规模高性能数据中心网络,全球数据中心互联_阿里云网络基线理解

TCP/UDP常见端口参考_怎么查看端口映射的是tcp还是udp-程序员宅基地

文章浏览阅读1.7k次。端口列表一览端口号码 / 层 名称 注释 1 tcpmux TCP 端口服务多路复用 5 rje 远程作业入口 7 echo Echo 服务 9 discard 用于连接测试的空服务 11 systat 用于列举连接了的端口的系统状态 13 daytime 给请求主机发送日期和时间 17 qotd 给连接了的主机发送每日格言 18 msp 消息发送协议 19 _怎么查看端口映射的是tcp还是udp

推荐文章

热门文章

相关标签