关于viewpage + FragmentPagerAdapter 当调用 notifydatasetchanged 造成其中的fragment内存泄漏

ViewPager 和 FragmentPagerAdapter 的使用中,调用 notifyDataSetChanged() 可能导致内存泄漏,这通常与 Android 系统管理 Fragment 的方式有关。为了深入理解这个问题,我们需要从 FragmentPagerAdapter 和 ViewPager 的源码入手。

FragmentPagerAdapter 和 FragmentStatePagerAdapter 的源码分析

FragmentPagerAdapter 源码分析

FragmentPagerAdapter 是 PagerAdapter 的子类,它主要用于管理少量静态页面。它的工作原理是将每个页面作为一个 Fragment 保存在内存中,并在需要时显示或隐藏它们。

以下是 FragmentPagerAdapter 中相关的部分源码:

 public abstract class FragmentPagerAdapter extends PagerAdapter {private static final String TAG = "FragmentPagerAdapter";private static final boolean DEBUG = false;private final FragmentManager mFragmentManager;private FragmentTransaction mCurTransaction = null;private Fragment mCurrentPrimaryItem = null;public FragmentPagerAdapter(FragmentManager fm) {mFragmentManager = fm;}@Overridepublic Object instantiateItem(ViewGroup container, int position) {if (mCurTransaction == null) {mCurTransaction = mFragmentManager.beginTransaction();}// 获取 Fragment 的标签final long itemId = getItemId(position);// 检查是否已经存在这个 FragmentString name = makeFragmentName(container.getId(), itemId);Fragment fragment = mFragmentManager.findFragmentByTag(name);if (fragment != null) {mCurTransaction.attach(fragment);} else {fragment = getItem(position);mCurTransaction.add(container.getId(), fragment, name);}if (fragment != mCurrentPrimaryItem) {fragment.setMenuVisibility(false);fragment.setUserVisibleHint(false);}return fragment;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {if (mCurTransaction == null) {mCurTransaction = mFragmentManager.beginTransaction();}mCurTransaction.detach((Fragment) object);}@Overridepublic void finishUpdate(ViewGroup container) {if (mCurTransaction != null) {mCurTransaction.commitNowAllowingStateLoss();mCurTransaction = null;}}@Overridepublic boolean isViewFromObject(View view, Object object) {return ((Fragment) object).getView() == view;}private static String makeFragmentName(int viewId, long id) {return "android:switcher:" + viewId + ":" + id;}
}

FragmentStatePagerAdapter 源码分析

FragmentStatePagerAdapter 是 FragmentPagerAdapter 的一个子类,更适合大量页面或动态变化的场景。它会在页面销毁时,销毁整个 Fragment 实例,而不仅仅是视图。

以下是 FragmentStatePagerAdapter 中相关的部分源码:

public abstract class FragmentStatePagerAdapter extends PagerAdapter {private static final String TAG = "FragmentStatePagerAdapter";private static final boolean DEBUG = false;private final FragmentManager mFragmentManager;private FragmentTransaction mCurTransaction = null;private ArrayList<Fragment> mFragments = new ArrayList<>();private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();public FragmentStatePagerAdapter(FragmentManager fm) {mFragmentManager = fm;}@Overridepublic Object instantiateItem(ViewGroup container, int position) {if (mFragments.size() > position) {Fragment f = mFragments.get(position);if (f != null) {return f;}}if (mCurTransaction == null) {mCurTransaction = mFragmentManager.beginTransaction();}Fragment fragment = getItem(position);if (mSavedState.size() > position) {Fragment.SavedState fss = mSavedState.get(position);if (fss != null) {fragment.setInitialSavedState(fss);}}while (mFragments.size() <= position) {mFragments.add(null);}fragment.setMenuVisibility(false);fragment.setUserVisibleHint(false);mFragments.set(position, fragment);mCurTransaction.add(container.getId(), fragment);return fragment;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {Fragment fragment = (Fragment) object;if (mCurTransaction == null) {mCurTransaction = mFragmentManager.beginTransaction();}while (mSavedState.size() <= position) {mSavedState.add(null);}mSavedState.set(position, fragment.isAdded() ?mFragmentManager.saveFragmentInstanceState(fragment) : null);mFragments.set(position, null);mCurTransaction.remove(fragment);}@Overridepublic void finishUpdate(ViewGroup container) {if (mCurTransaction != null) {mCurTransaction.commitNowAllowingStateLoss();mCurTransaction = null;}}@Overridepublic boolean isViewFromObject(View view, Object object) {return ((Fragment) object).getView() == view;}
}

内存泄漏的根本原因

从上述源码分析可以看到,内存泄漏的根本原因是 FragmentPagerAdapter 并不会销毁 Fragment 实例,只会分离它们的视图,而 FragmentStatePagerAdapter 则会销毁整个 Fragment 实例。

  1. FragmentPagerAdapter 的行为
    FragmentPagerAdapter 在 destroyItem 方法中调用 detach,只是分离了 Fragment 的视图,没有销毁 Fragment 实例。这意味着 Fragment 实例仍然存在于内存中,可能会导致内存泄漏,尤其是在频繁更新页面内容时。
  2. FragmentStatePagerAdapter 的行为
    FragmentStatePagerAdapter 在 destroyItem 方法中调用 remove,销毁整个 Fragment 实例,并保存其状态。在需要时重新创建 Fragment 实例。这种方式更适合动态页面的情况,可以避免内存泄漏。

解决方案一:使用 FragmentStatePagerAdapter

对于页面数量较多或内容动态变化的情况,使用 FragmentStatePagerAdapter 可以有效避免内存泄漏,因为它会销毁整个 Fragment 实例。
在 FragmentPagerAdapter 中手动管理 Fragment 的生命周期

如果必须使用 FragmentPagerAdapter,确保在调用 notifyDataSetChanged() 时,手动管理 Fragment 的生命周期,正确移除不再需要的 Fragment 实例。

@Override
public void destroyItem(ViewGroup container, int position, Object object) {Fragment fragment = (Fragment) object;if (mCurTransaction == null) {mCurTransaction = mFragmentManager.beginTransaction();}mCurTransaction.remove(fragment);
}

避免对 Activity 或 Fragment 的强引用
在 Fragment 中避免持有对 Activity 的强引用,使用弱引用或在适当的时候手动清理引用。

解决方案二: 手动管理 Fragment 的生命周期

虽然 FragmentPagerAdapter 不会自动销毁 Fragment 实例,我们可以在notifydatasetchanged 之前,对adapter中的 fragment 进行手动移除 但你可以在 destroyItem() 方法中手动移除 Fragment。这样可以确保 Fragment 不会继续占用内存。

@Override
public void destroyItem(ViewGroup container, int position, Object object) {Fragment fragment = (Fragment) object;FragmentManager fragmentManager = fragment.getFragmentManager();if (fragmentManager != null) {FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();fragmentTransaction.remove(fragment);fragmentTransaction.commitNowAllowingStateLoss();}
}

最后

我们需要注意:

1.在 Fragment 中避免持有对 Activity 的强引用

Fragment 持有 Activity 的强引用会导致内存泄漏。在 Fragment 中可以使用弱引用来持有 Activity。

public class MyFragment extends Fragment {private WeakReference<Activity> activityReference;@Overridepublic void onAttach(Activity activity) {super.onAttach(activity);activityReference = new WeakReference<>(activity);}@Overridepublic void onDetach() {super.onDetach();activityReference.clear();}private Activity getActivityReference() {return activityReference.get();}
}
2.在 Fragment 中避免静态引用

避免在 Fragment 中使用静态变量持有 Activity 或 View 的引用,因为静态变量的生命周期与应用程序一致,可能会导致内存泄漏。

3.清理未使用的 Fragment

确保在不再需要某个 Fragment 时,手动清理它。例如,在 onDestroy() 或其他适当的位置,确保引用被清理。

@Override
public void onDestroy() {super.onDestroy();// 清理未使用的 Fragmentif (myFragment != null) {FragmentManager fragmentManager = getFragmentManager();if (fragmentManager != null) {FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();fragmentTransaction.remove(myFragment);fragmentTransaction.commitNowAllowingStateLoss();}}
}
4.适当地使用 setRetainInstance(false)

如果 Fragment 设置了 setRetainInstance(true),会导致 Fragment 在 Activity 重建时保留,可能会导致内存泄漏。在不需要保留 Fragment 实例时,确保 setRetainInstance(false)。

复制代码
@Override
public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setRetainInstance(false); // 确保 Fragment 不被保留
}
5.使用 WeakReference 或其他方式避免对外部资源的强引用

在 Fragment 中,尽量使用 WeakReference 或其他方式避免对外部资源(如大对象、静态资源、上下文等)的强引用。

public class MyFragment extends Fragment {private WeakReference<Context> contextReference;@Overridepublic void onAttach(Context context) {super.onAttach(context);contextReference = new WeakReference<>(context);}private Context getContextReference() {return contextReference.get();}
}
6.使用 FragmentManager 正确管理 Fragment

在需要替换或移除 Fragment 时,使用 FragmentManager 和 FragmentTransaction 正确管理 Fragment 的生命周期。

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = fragmentManager.findFragmentByTag("TAG");
if (fragment != null) {fragmentTransaction.remove(fragment);
}

fragmentTransaction.commitNowAllowingStateLoss();

7. 避免 Fragment 中长生命周期的任务

在 Fragment 中避免执行长生命周期的任务,如异步任务、后台线程等。如果必须执行,确保在 Fragment 销毁时取消或停止这些任务。

@Override
public void onDestroy() {super.onDestroy();// 取消或停止长生命周期的任务if (asyncTask != null) {asyncTask.cancel(true);}
}

通过以上措施,可以有效减少或避免在使用 FragmentPagerAdapter 时导致的内存泄漏问题。确保在 Fragment 和 Activity 的生命周期中正确管理资源和引用,可以显著提高应用的内存使用效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/36561.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARM裸机:地址映射

S5PV210的地址映射详解 什么是地址映射&#xff1f; S5PV210属于ARM Cortex-A8架构&#xff0c;32位CPU&#xff0c;CPU设计时就有32根地址线&32根数据线。 32根地址线决定了CPU的地址空间为4G&#xff0c;那么这4G空间如何分配使用&#xff1f;这个问题就是内存映射问题。…

NAND闪存巨头铠侠(Kioxia)计划最迟于10月下旬通过首次公开募股IPO

据路透社于6月26日引用消息来源的报道&#xff0c;在半导体市场条件反弹及财务业绩迅速改善的背景下&#xff0c;NAND闪存巨头铠侠&#xff08;Kioxia&#xff09;正准备尽快提交初步申请&#xff0c;并计划最迟于10月下旬通过首次公开募股&#xff08;IPO&#xff09;在东京证…

9.二维数组的遍历和存储

二维数组的遍历和存储 二维数组的遍历 二维数组a[3][4],可分解为三个一维数组,其数组名分别为: 这三个一维数组都有4个元素,例如:一维数组a[0]的 元素为a[0][0],a[0][1],a[0][2],a[0][3]。所以遍历二维数组无非就是先取出二维数组中得一维数组, 然后再从一维数组中取出每个元…

Eclipse代码编辑器自主配色

1. 打开 Eclipse 的设置 - Java - Editor - Syntax Coloring 2. 自定义各种类型的颜色&#xff0c;例如&#xff1a; 1. Interface 勾选&#xff0c;设置为紫色 2. Class 勾选&#xff0c;设置为淡蓝色 3. Abstract classes 勾选&#xff0c;有自己默认的颜色 …

俯视LLM的灵魂:一文搞懂稀疏自动编码器

实时了解业内动态&#xff0c;论文是最好的桥梁&#xff0c;专栏精选论文重点解读热点论文&#xff0c;围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型重新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;…

使用jupyter打开本地ipynb文件的方法

常用方法&#xff1a; 先启动jupyter&#xff0c;然后在打开的页面点击upload&#xff0c;选择想要打开的文件上传然后打开&#xff0c;但是这样其实是先复制了一份到jupyter中&#xff0c;然后打开运行。而我不想复制。 方法二 先打开项目文件所在文件夹&#xff0c;文件夹…

【Dison夏令营 Day 01】如何用Python写一个游戏“石头剪刀布”

欢迎你们踏入这个充满无限可能性的编程世界&#xff01;作为一名热爱Python的开发者&#xff0c;我深感编程的魅力&#xff0c;并希望与你们一同分享这份乐趣和激情。编程&#xff0c;不仅仅是一种技能&#xff0c;更是一种思维方式和解决问题的工具。在Python的世界里&#xf…

118. 杨辉三角

118. 杨辉三角 题目链接&#xff1a;118. 杨辉三角 代码如下&#xff1a; class Solution { public:vector<vector<int>> generate(int numRows) {vector<vector<int>> res(numRows);for(int i0;i<numRows;i){res[i].resize(i1,1);for(int j1;j&…

RuntimeError: “exp_vml_cpu“ not implemented for ‘Half‘

遇到 "exp_vml_cpu" not implemented for Half 这个运行时错误&#xff0c;意味着你尝试在一个操作中使用了半精度&#xff08;Half 或 float16&#xff09;数据类型&#xff0c;但是该操作在当前环境下并没有针对半精度数据类型的实现。 半精度&#xff08;float16&…

如何寻找强势货币和弱势货币?

外汇交易的独特之处在于&#xff0c;它融合了两种货币的价值&#xff0c;其中一种货币的价值通过另一种货币来体现。举例来说&#xff0c;USDJPY外汇反映了美元与日元之间的价值关系&#xff0c;而EURUSD则代表了欧元与美元的价值对比。 通过开仓操作&#xff0c;我们预测一种…

Linux学习第54天:Linux WIFI 驱动:蓝星互联

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 数字化、现代化的今天&#xff0c;随处的WIFI给与了大众极大的方便&#xff0c;也感受到了科技的力量。万物互联、无线互联越来越成为一个不可逆转的趋势。现在比较火…

四川赤橙宏海商务信息咨询有限公司抖音电商领航者

在数字化浪潮席卷全球的今天&#xff0c;电商行业无疑是其中最为活跃、最具潜力的领域之一。而在中国这片广袤的土地上&#xff0c;四川赤橙宏海商务信息咨询有限公司以其独特的视角和前瞻性的战略布局&#xff0c;成为了抖音电商服务领域的佼佼者。今天&#xff0c;就让我们一…

stm32学习笔记---TIM输入捕获(代码部分)输入捕获模式测频率/PWMI模式测频率占空比

目录 第一个代码&#xff1a;输入捕获模式测频率 调整频率 PWM.c PWM.h 输入捕获 IC.c 输入捕获初始化步骤 TIM.h库函数 TIM_ICInit TIM_PWMIConfig TIM_ICStructInit TIM_SelectInputTrigger TIM_SelectOutputTrigger TIM_SelectSlaveMode 单独配置四个通道的分…

三、如何使得插件可以跨平台和独立测试和运行,从而支持完美解耦

要使插件能够跨平台运行、独立测试和运行&#xff0c;从而实现完美解耦&#xff0c;可以遵循以下步骤和最佳实践&#xff1a; 1. 设计良好的接口和API 抽象接口&#xff1a;定义清晰、稳定的接口&#xff0c;这些接口应该封装插件的核心功能&#xff0c;而不暴露实现细节。依…

聊一聊UDF/UDTF/UDAF是什么,开发要点及如何使用?

背景介绍 UDF来源于Hive&#xff0c;Hive可以允许用户编写自己定义的函数UDF&#xff0c;然后在查询中进行使用。星环Inceptor中的UDF开发规范与Hive相同&#xff0c;目前有3种UDF&#xff1a; A. UDF--以单个数据行为参数&#xff0c;输出单个数据行&#xff1b; UDF&#…

深度强化学习之SAC(Soft Actor-Critic)

SAC(Soft Actor-Critic)是一种深度强化学习算法,结合了最大熵强化学习和基于策略梯度的方法。SAC通过最大化期望回报和策略熵,使得策略既能探索环境又能利用已知信息,从而提高了策略的稳定性和性能。 SAC的基本概念 强化学习(RL): 强化学习旨在通过与环境交互,学习能…

打破生态「孤岛」,Catizen将开启Telegram小游戏2.0时代?

Catizen&#xff1a;引领Telegram x TON生态的顶级猫咪链游 在区块链游戏领域&#xff0c;吸引玩家的首要因素往往是游戏的趣味性。然而&#xff0c;仅靠趣味性无法评估一个项目的长期价值和发展潜力。真正能在区块链游戏市场中取得长久成功的项目&#xff0c;无一例外都依靠扎…

Spring Boot中的表单处理

Spring Boot中的表单处理 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在现代的Web应用程序开发中&#xff0c;表单处理是不可或缺的一部分。Spring Boot作…

【消息队列】RabbitMQ集群原理与搭建

目录 前言1、集群搭建1.1、安装RabbitMQ1.1.1、前置要求1.1.2、安装Erlang环境①创建yum库配置文件②加入配置内容③更新yum库④正式安装Erlang 1.1.3、安装RabbitMQ1.1.4、RabbitMQ基础配置1.1.5、收尾工作 1.2、克隆VMWare虚拟机1.2.1、目标1.2.2、克隆虚拟机1.2.3、给新机设…

智能充电桩网关,构建高效充电网络

近年来我国新能源汽车的增长速度出现明显的上升趋势&#xff0c;但是其充电桩的发展还比较缓慢。目前在充电桩系统设计期间仍存在一些问题&#xff0c;主要表现在充电设施短缺、充电难等问题&#xff0c;这些问题的发生均会在一定程度上限制新能源汽车的发展&#xff0c;这就需…