Fragment has not been attached yet Fragment 套 Fragment

Fragment has not been attached yet

Fragment 套 Fragment

在商城项目中使用了 Fragment 套 Fragment的结构,大致框架如下图
在这里插入图片描述
MallActivity中FragmentStateAdapter的参数为“this”,SortMallFragment的简单代码如下:

public class SortMallFragment extends Fragment implements View.OnClickListener{
               ... ...
    @Override
    public View onCreateView(LayoutInflater inflater,
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View sortView = inflater.inflate(R.layout.sort_mall_fragment, container, false);
                ... ...
        sortViewPager2 = sortView.findViewById(R.id.sort_view_pager2);
        sortViewPager2.setAdapter(fsa);
        return sortView;
    }
                ... ...
    private FragmentManager fm = getChildFragmentManager();
    private Lifecycle lc =  getLifecycle();
    private FragmentStateAdapter fsa = new FragmentStateAdapter(fm, lc) {
                 ... ...
    }
}

运行之后崩溃,错误提示如下:

                ... ...
Fragment SortMallFragment{7138fb4} (e5c24c20-127c-4821-9707-9ea4cc48e5e7)} has not been attached yet.
        at androidx.fragment.app.Fragment.getChildFragmentManager(Fragment.java:980)
        at com.example.baigyueg.fragment.mallFragment.SortMallFragment.<init>(SortMallFragment.java:183)
        at com.example.baigyueg.fragment.mallFragment.SortMallFragment.newInstance(SortMallFragment.java:42)
        at com.example.baigyueg.activity.mall.MallActivity.onCreate(MallActivity.java:97)
                 ... ...

网上一顿狂搜,发现有关“Fragment has not been attached yet”的条目很少,并且不太符合本项目的情况,参考价值不大。又反过来研究错误提示,点一下“(Fragment.java:980)”,发现进入了Fragment的源码:

@NonNull
    final public FragmentManager getChildFragmentManager() {
        if (mHost == null) {
            throw new IllegalStateException("Fragment " + this + " has not been attached yet.");
        }
        return mChildFragmentManager;
    }

大概意思就是当mHostd = null时,就抛出“Fragment ······has not been attached yet”,否则就生成一个“子FragmentManager”。那Fragment中的这个mHost又是什么“东西呢”?再进一步查mHost,就渐渐坠入“迷雾”了——越查越深,新手,知道的东西太少了!但这一查,也有一些小收获:
1、在Fragment中管理其子Fragment要使用ChildFragmentManager,其生成方法getChildFragmentManager()就在Fragment中,在Fragment中使用(又套Fragment)时直接写成:

FragmentManager fm = getChildFragmentManager();

2、Fragment总是要“寄生”或“附着”在什么东西上,比如寄生在活动上或寄生在碎片上,所以Fragment生命周期第一个回调方法就是onAttach(),即先“附着”上去再往下进行。那么错误提示是不是在说本碎片(SortMallFragment)还没有附着在它的宿主(MallActivity)身上?
好,用Log.d(,)测试一下,结果onAttach()已经被执行,那为什么还提示“has not been attached yet”呢?是不是虽然执行了onAttach(),但仍未附着呢?
之前未套子Fragmeng时,运行并不崩溃,是不是在主碎片(SortMallFragment)中套入了子碎片后引起了崩溃?那好先屏蔽掉所有关于子碎片的代码,再测试,未崩溃,为什么?其实也好理解,主碎片中mHost=null,无法生成子碎片管理器(ChildFragmentManager),所以产生了崩溃。问题又回到了原点:到底什么是mHost?真令人头疼!

再梳理一下:
1、网上搜索此错误提示相关条目少,说明要么是Android的一个bug,可能性小,因为碎片嵌套太常用了,若是bug,早被修复了;要么是自己的代码不符合常规的要求(较奇葩),别人都不这样做,所以少见。
2、必须明确当前探索的方向。当前最明显,最强烈的提示还是官方提示“Fragment has not been attached yet”,那好吧,“网过千遍有漏鱼”,就“认真地”再网搜一遍吧!
这次把心沉下来,一条一条地过!发现有位大神说把子碎片相关代码放入主碎片的onStart()方法中可能会解决问题,之前已经搜到过这位大神的建议,但没没有认真思考,就放过去了,好吧,再试试。

public class SortMallFragment extends Fragment implements View.OnClickListener{
               ... ...
    @Override
    public void onStart() {
        super.onStart();
        FragmentManager fm = getChildFragmentManager();
        Lifecycle lc =  getLifecycle();
        FragmentStateAdapter fsa = new FragmentStateAdapter(fm, lc)
    }
    @Override
    public View onCreateView(LayoutInflater inflater,
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View sortView = inflater.inflate(R.layout.sort_mall_fragment, container, false);
                ... ...
        sortViewPager2 = sortView.findViewById(R.id.sort_view_pager2);
        sortViewPager2.setAdapter(fsa);
        return sortView;
    } 
                ... ...              
}

结果,真神奇,居然没有崩溃, 顺利地进入了子碎片,真令人欣喜,困扰多天的问题就这样解决了!欣喜之余又想,为什么是放在onStart()中,放在碎片的其它回调方法中行不?于是又测试放在onCreateView()、onViewCreated()、onActivityCreated()中,也行,那问题来了,到底放在哪个方法更好呢?通过查看developers网站ViewPager2中的“用户指南”中的代码示例发现,官方只将启动布局文件,并返回对应的view的代码放在onCreateView()中,将生成生成适配器、设置设配器的代码都放在了onViewCreated()中。此两方法前者返回的view就是后者参数中的view。那好吧,比葫芦画瓢,老老实实按官方推荐的去做吧:

public class SortMallFragment extends Fragment implements View.OnClickListener{
               ... ...
    @Override
    public View onCreateView(LayoutInflater inflater,
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View sortView = inflater.inflate(R.layout.sort_mall_fragment, container, false); 
        return  sortView;  
    } 
    
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
                  ... ...
        sortViewPager2 = view.findViewById(R.id.sort_view_pager2);
        FragmentManager fm = getChildFragmentManager();
        Lifecycle lc =  getLifecycle();
        FragmentStateAdapter fsa = new FragmentStateAdapter(fm, lc)
        sortViewPager2.setAdapter(fsa);
    }
                   ... ...              
}

问题解决。那到底是什么原因造成“Fragment has not been attached yet”?仔细观察最上面的代码会发现如下代码:

    private FragmentManager fm = getChildFragmentManager();
    private Lifecycle lc =  getLifecycle();
    private FragmentStateAdapter fsa = new FragmentStateAdapter(fm, lc) {
                 ... ...
    }

写在了在回调方法之外,问题就出在这里。在“活”的类(如活动、碎片)中,对象的声明写在回调方法之外,对象的实现(get、set、new等)写在回调方法中,或者干脆声明、实现全部写在回调方法中,这是常识,而我违反了这个常识,将子碎片管理器、主碎片的适配器的声明、实现都写在了回调方法之外。我在想,其中的机理是不是这样的:这些对象的生成往往需要回调方法所在的“活体系”运行时产生的某些数据,不放入这个体系中,就得不到这些数据,产生对象就是“不完美的”,或“过时的”,从而引起报错?总之,在活动、碎片等“活”类中,尽量将对象的实现放在回调方法中

关于类、生命周期的一些个人理解
类好比一个“袋子”,有的袋子只装了一些工具(方法等);有些袋子(如活动、碎片)里除了工具外,还装了一条具有活力的“串珠”,串珠上每个“珠子”就是一个回调方法,程序按顺序从第一个“珠子”开始执行,直到最后一个珠子,这就是生命周期。生命周期是编程高度“模式化”的体现,可以提高编程质量和速度,若用不好,会起副作用,如本例。

关于攻坚克难的一些想法
1、解决难题最简单、最有效的方法:别停下来。
2、解决困难要讲求原则、方法。
3、艰难险阻不一定都是坏事。困难是向上攀登的阶梯,最好的学习、最深刻的记忆是克服困难,解决问题。勇敢地迎接下一个困难吧!

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值