FragmentPagerAdapter 的那些坑

Android - 一种新奇的冷启动速度优化思路(Fragment极度懒加载 + Layout子线程预加载) 众所周知,Context是一个抽象类,实现它的包装类就是ContextWrapper,而Activity、Appcation等都是ContextWrapper的子类,然而,ContextWrapper还有一个神奇的子类,直接看图,这个是首页四个子Tab Fragment的基类的layout,因为某些东西设计的不合理,导致层级是非常的深,直接导致了首页上的三个tab加上FeedMainFragment自身,光将这个View inflate出来的时间就非常长。因此,我们就产生了懒加载这样一个说法。 阅读详情

引子

image

开始前,首先来看一下这种常用的布局,每个底部导航栏项中呢都有一个ViewPager。
笔者先来说一下自己遇到的问题,第一次进入的时候ViewPager(第一页)显示正常,但是切换到别的底部tab再切换回来的时候,ViewPager的第一项和第二项的视图就没有了,试过FragmentPagerAdapter 和 FragmentStatePagerAdapter 但都没有解决问题,所以笔者决定从源码的角度出发,去探索问题所在。

起始

首先
底部Tab1是HomeFragement
底部Tab2是OrdersFragment

由HomeFragment切换到OrdersFragment之后的周期调用情况如下:

— 后台打印如下 —
07-29 11:21:56.560 18830-18830/com.finobusiness.forhomelife D/HomeFragment: onDestroyView:
07-29 11:21:56.565 18830-18830/com.finobusiness.forhomelife D/OrdersFragment: onCreateView:

HomeFragment只是调用了onDestroyView方法,没有调用onDestroy方法。

问题源头

FragmentPagerAdapter 的instantiateItem()方法如下:

 @Override
    public Object instantiateItem(ViewGroup container, int position) {

        final long itemId = getItemId(position);

        // Do we already have this fragment?
        String 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,
                    makeFragmentName(container.getId(), itemId));
        }

        return fragment;
    }

需要注意的是这段代码:

        Fragment fragment = mFragmentManager.findFragmentByTag(name);

可以看到在去实例化一个Fragment之前会先去FragmentManager当中通过Tag查找是否已经有了这个Fragment,如果有,则不再调用public abstract Fragment getItem(int position) 这个方法,通常这是咱们实现的用来提供Fragment的方法。

Fragment 的 onDestroyView()

因为ViewPager当中的Fragment的视图已经被销毁了

onDestroyView的源码

    public void onDestroyView() {
        mCalled = true;
    }

没错,只有这么一行,但是里面的mCalled威力可是很惊人的,一会儿就会涉及到。
在代码中查询,发现onDestroyView()方法在全局中只有一个地方被调用到了,那就是

 void performDestroyView() {
        if (mChildFragmentManager != null) {
            mChildFragmentManager.dispatchDestroyView();
        }
        mState = CREATED;
        mCalled = false;
        onDestroyView();
        if (!mCalled) {
            throw new SuperNotCalledException("Fragment " + this
                    + " did not call through to super.onDestroyView()");
        }
        if (mLoaderManager != null) {
            mLoaderManager.doReportNextStart();
        }
    }

首先呢可以看到如果onDestroyView()方法在在子类中没有被调用,则会抛出SuperNotCalledException异常

其次

mChildFragmentManager.dispatchDestroyView 最终会调用到FragmentManager的void moveToState(int newState, int transit, int transitStyle, boolean always)方法。

moveToState方法的源码如下

    void moveToState(int newState, int transit, int transitStyle, boolean always) {
        mCurState = newState;
        if (mActive != null) {
            boolean loadersRunning = false;
            for (int i=0; i<mActive.size(); i++) {
                Fragment f = mActive.get(i);
                if (f != null) moveToState(f, newState, transit, transitStyle, false);
            }
        }
    }
    case Fragment.ACTIVITY_CREATED:
            if (newState < Fragment.ACTIVITY_CREATED) {

                f.performDestroyView();
                if (f.mView != null && f.mContainer != null) {
                    f.mContainer.removeView(f.mView);
                }
                f.mContainer = null;
                f.mView = null;
                f.mInnerView = null;
    }

最终可以看到fragment.view被移除并且被置为null。
下一次进来的时候Fragment被通过getFragmentByTag方法找到,但是他的View却是空,所以我们的ViewPager的前两页(默认的缓存页)就没有视图。

解决之道

解决的办法就是ViewPager所在的那个Fragment中手动调用PagerAdapter 的destroyItem方法

FragmentPagerAdapter更换、刷新fragment FragmentPagerAdapter可更换fragment、也可刷新fragment 立即下载

相关推荐

火星遥感数据的获取与处理工具安装详解

有关火星遥感影像数据的那些事...

FengYingLuo的博客 5573

FragmentPagerAdapter被弃用后的代替方案

FragmentPagerAdapter被弃用后的代替方案完整解决方案。

u011501017的博客 6733

S32K3基于EB配置GPT触发WDG的实战解析

本文详细解析了S32K3 MCU基于EB tresos工具配置GPT定时器触发看门狗(WDG)的实战方法。通过分析协同工作机制、MCAL配置步骤及代码实现,帮助开发者构建可靠的硬件监控系统,提升汽车电子系统的安全性与实时性。

f9g0h的博客 205

ViewPager替代----ViewPager2

ViewPage,主要作用是左右切换当前的view,实现滑动切换的效果,开始动手之后才发现,ViewPager的两个适配器FragmentPagerAdapter和FragmentStatePagerAdapter都被谷歌弃用了,故此在网上查了下平替,就看到了ViewPager2。其中,滑动切换图片是新内容,同时结合其他几个组件回顾一下前面所学。可以看到,目前评论内容也是定死在了前台布局,后续再把数据库学完后,再结合Listview等,逐渐形成一个更“像样”的模块。图1:被弃用了的好哥俩。

weixin_44067776的博客 780

HFSS实战:手把手教你设计微带发夹线滤波器(附完整参数计算流程)

本文详细介绍了使用HFSS设计微带发夹线滤波器的完整流程,包括参数计算、HFSS建模和优化策略。通过实战案例,帮助读者掌握微波滤波器设计的核心技巧,特别是奇偶模阻抗计算和HFSS仿真中的关键设置,适用于通信系统设计和射频工程应用。

weixin_29251567的博客 441

EXI编解码避坑指南:ISO15118-2协议中3条异常命令的差异分析

本文深度剖析了ISO15118-2协议测试中EXI编解码的典型问题,聚焦于ChargeParameterDiscoveryRes、CertificateInstallationReq和MeteringReceiptReq三条命令在不同工具间产生109字节差异的现象。通过对比EXICodec.jar、OpenEXI和Efficient XML的输出,揭示了差异根源于EXI报头处理机制与模式文件完整性,并提供了从现象确认、根因分析到解决方案的标准化排查流程,助力协议测试工程师确保编解码一致性与通信可靠性。

algae的博客 411

FragmentPagerAdapter(androidx.fragment.app.FragmentManager)‘ 已被弃用

源代码 public MyAdapter(FragmentManager fragmentManager) { super(fragmentManager); } 改为 public MyAdapter(FragmentManager fragmentManager) { super(fragmentManager,FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);

qq_38972615的博客 4660

基于强化学习的工业SCR脱硝系统控制算法设计与实现

本文提出了一种基于强化学习的工业SCR脱硝系统智能控制方法。针对SCR系统存在的非线性、大滞后和多变量耦合等控制难点,研究采用混合建模方法构建了精度在5%以内的系统模型,并设计了专门的强化学习环境。该方法通过定义合理的动作空间和状态空间,结合物理约束设计损失函数,利用深度强化学习算法优化氨喷射控制策略。相比传统PID控制,该智能控制器能更好地适应烟气参数波动,实现NOx排放的稳定控制,为工业环保设施提供了更优的自动化解决方案。

huanghm88的专栏 1456

ViewPager+Fragment+FragmentPagerAdapter,Fragment无法被销毁

ViewPager+Fragment+FragmentPagerAdapter,Fragment无法被销毁

qq_25330791的博客 828

PyTorch 面试题及参考答案(精选100道)

模块是 PyTorch 中用于自动求导的核心模块。它为张量上的所有操作提供了自动求导的功能。在深度学习中,反向传播是一种用于计算损失函数相对于模型参数的梯度的算法。通过梯度,我们可以使用优化算法(如随机梯度下降)来更新模型的参数,从而最小化损失函数。模块的主要作用就是自动计算这些梯度。当我们创建一个张量时,如果将其属性设置为True,那么 PyTorch 会跟踪该张量上的所有操作。在完成前向传播计算出损失函数后,调用方法,模块会自动根据链式法则反向传播计算出所有需要求导的张量的梯度。

大模型大数据攻城狮的专栏 1681

SAP Group Reporting入门指南(8)--业务操作

关于资产负债表类的匹配,系统预设了SGR01和SGR02两个方法,主要区别在于数据的来源范围,SGR01取自ACDOCA和ACDOCU,如果是0F类型的就会直接显示日记账分录(期间是000的除外,这类凭证是年结结转的,系统不再跨年追溯),如果是其他类型就显示合并分录。这里是我觉得SAP还需要优化的地方。基于ICMR的抵消任务中通常将抵销差异细分为交易差异、货币换算差异和其他差异,这在基于重分类抵消任务是无法实现的,基于重分类抵消只知道一个差异总额,很难从合并抵消凭证看出差异的来源,也没有审计线索可言。

bflyfish的博客 276

ViewPager使用FragmentPagerAdapter 踩坑-不能动态删除Fragment

很多时候我们使用ViewPager来包含Fragment的时候,都是使用FragmentPagerAdapter,大致如下,确实用起来采用官方封装好的PagerAdapter也很简单方便 public class TabFragmentPagerAdapter extends FragmentPagerAdapter { private List&lt;Fragment&gt; mF...

ddxxii的博客 9845

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

viewpager布局复用中FragmentPagerAdapter的坑,源码分析,控件id的一些思考

一个fragment的布局复用,里面是tablayout+viewpager,viewpager加载不同adapter,adapter继承FragmentPageAdapter。运行后有问题,先初始化的fragment正常显示,后加载的fragment里的viewpager全部是空白,这就很尴尬了,第一反应是fragment没add进FragmentManager,因为在同一个activity里,所

秋月夜的博客 2932

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

ViewPager + FragmentPagerAdapter 实现浏览器多窗口遇到的坑

在使用webView开发一个简易浏览器的过程中,需要完成一个浏览器多窗口的功能,我选择使用Viewpager +FragmentPagerAdapter 来实现该功能,但是开发过程中,遇到了下面这两个彼此冲突的问题: 1. 可以成功新建或删除窗口,但是,在新建或删除一个窗口时,其他Fragment中的内容会被刷新,作为浏览器,这样的效果无法令人接受。 2. Frag...

AMD556的博客 556

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

android fragmentpageradapter切换不更新,更新及替换ViewPager中的Fragment 遇到的坑

下图是要实现的效果:星梦缘.png结果我实现的结果:QQ图片20170323170637.pngQQ图片20170323170644.png切换上面的按钮,我希望能够通过调mAdapter.notifyDataSetChanged() 来触发 Fragment 页面使用新的数据调整或重新生成其内容,可是当我调用 notifyDataSetChanged() 后,发现什么都没发生。fragment的...

weixin_36272622的博客 1489

Fragment嵌套带来的坑--页面点击无反应(顺带ViewPager之 FragmentPagerAdapter简单分析)

接手别人的老项目。新版本测试提出一个bug:点击Home最小化的应用—>系统设置界面 改变字体后—>点击进入应用—>3个由viewpager 的fragmentadapter管理的 tab页面点击都没反应。这是一个比较蛋疼的bug,猜想了很多原因,都不对。 项目的结构是 activity 内有mainfragment,mainfragment又 包含viewpager,viewpager 使用Fr

tmac2000的专栏 3779

FragmentPagerAdapter 更新数据遇到了坑啊。下拉刷新不会更新页面、有缓存。

ViewPager+FragmentPagerAdapter:更新Fragment里数据是不起作用,FragmentPagerAdapter添加或减少Fragment时,前面的Fragment内容更新不起作用等等问题,有的做法是暴力删除fragment列表 粗暴解决方案1: List<Fragment> fragments = getSupportFragm

非鹭千里 1197

Android 使用FragmentPagerAdapter懒加载时的坑

       Fragment+ViewPager的方式相信大家经常使用了,但是也有不少的问题。首先就是如果Fragment经常销毁再创建,那样在左右切换的时候界面会显得非常卡顿。         所以我设置setOffscreenPageLimit(count-1),Fragment在add一次后,不再add和remove。         但是一次加载所有的Fragment,尤其当Frag...

zjf375215380的专栏 2053

FragmentPagerAdapter加载fragment并使用setUserVisibleHint()处理预加载时遇到的坑,给textview赋值时出现的空指针异常

FragmentPagerAdapter加载fragment并使用setUserVisibleHint()处理预加载时遇到的坑,给textview赋值时出现的空指针异常

qq_38472465的博客 175

FragmentPagerAdapter加载fragment并使用setUserVisibleHint()处理预加载时遇到的坑,给textview赋值时出现的空指针异常...

FragmentPagerAdapter加载fragment并使用setUserVisibleHint()处理预加载时,给textview赋值时出现的空指针异常 public class BaseFragment extends Fragment { /*布局*/ private View view; /**/ boolean isVisible = ...

weixin_30363817的博客 214

android pageradapter页面缓存,Android ViewPager Adapter 内存管理的坑

好久没有记录bug了的,因为最近一直在解bug... 之所以写这个文章,确实是出于无奈,毕竟同一个问题,项目上遇到了两次。这里主要是提一下 ViewPager + Fragment。ViewPager + Fragment 有 '两种' AdapterFragmentPagerAdapterhttps://developer.android.com/reference/android/suppor...

weixin_39719077的博客 386

Android基础知识-Fragment相关

https://www.jianshu.com/p/d9143a92ad94 https://www.jianshu.com/p/fd71d65f0ec6 一、Fragment生命周期 https://www.jianshu.com/p/927ca066120b 1、定义 Fragment 表示 FragmentActivity 中的行为或界面的一部分。 2、Fragment优点: 与Activi...

笨鸟的专栏 1131

Android知识笔记:记录一个至今仍有很多人写错的技术点,避免入坑!

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!资料⬅专栏获取。

2401_85729503的博客 494

FragmentPagerAdapter使用

单独使用Fragment经常会遇到一些问题,比如Fragment切换时,显示和隐藏的Fragment有时候会出现重叠,导致相互之间的UI和事件混乱。 结合ViewPager的使用,则可以避免掉大部分的问题。Google基于PagerAdapter添加了FragmentPagerAdapter类来解决Fragment与ViewPager之间的配合问题。 FragmentPagerAdap

qq_33682144的博客 644
上一篇: 策略模式 - Android.TabHost
下一篇: Android知识体系图
氢电公敌
博客等级 码龄11年 3粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值