Fragment 的add和replace黑屏/白屏问题探究

问题描述:

在使用mvp等模式进行开发时,有时会使用单Activity嵌套单Fragment进行业务解耦,往往会在Activity onCreated的时候,直接通过add或者replace直接添加子Fragment,这时候就会发现,在系统资源紧张的情况下,或者在后台运算过于频繁的时候,易出现卡顿,黑屏/白屏的问题,具体现象是整个屏幕完全黑掉或者白掉,几秒后,界面才显示出来,并且通过修改theme并不能解决这个黑白屏问题。

问题原因:

问题的核心就是在于以下代码:

 supportFragmentManager
            .beginTransaction()
            .replace(resId, fragment)
            .commitAllowingStateLoss()

不要迷信啥add代替replace会降低性能要求之类的,如果你的Activity只是单个Fragment填充的话,那add代替replace就基本没有意义了,所以再咋修改这里的乱七八糟的方案都不好使。

问题的核心点在于,单Fragment做的ui过于复杂了(view树过深或者自定义View在初始化的时候做了太多乱七八糟的操作了),add或者replace的时候会调Fragment的生命周期拿view,这时候就会因性能问题把屏幕卡死(具体是那块代码或者深入原因导致的我还没研究清楚,希望有大神能提点一下),所以,核心是让这个方法返回的界面尽可能简单点,渲染快一点

  @MainThread
    @Nullable
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        if (mContentLayoutId != 0) {
            return inflater.inflate(mContentLayoutId, container, false);
        }
        return null;
    }

解决方案

最好的解决方案就是写ui的时候注意点,尽量降低View树的层级,或者是多Fragment嵌套,尽量使View绘制的不那么麻烦,或者干脆直接在Activity里码代码,放弃这个倒霉催的架构。但是对于大多数已经出现这个问题的小伙伴们来说,往往都是这个界面进过多次迭代之后,代码莫名其妙的垒到这么多,忽然就开始黑屏了,这时候再去往外摘,显然是个有心无力的大工程。

像上边说的这种情况,解决思路有两种,核心是使用ViewStub进行延迟加载:

  1. 把整个界面全部使用ViewStub包裹,然后在onCreateView的时候,用协程也好,RxJava也好,做个延迟,绕过
    onCreateView这个时机,如:
   override fun initView(parent: View?) {
        //延迟初始化,防止replace fragment 导致黑屏
        viewLifecycleOwner.lifecycleScope
            .launch(Dispatchers.Main) {
                delay(100)
                //先注册监听,要不不好使
                vs_content.setOnInflateListener { _, _ ->
                    //在这才是真正的view初始化完成,开始写逻辑,要不拿的view空指针
                }
                vs_content.inflate()
            }
    }

看不懂kotlin和协程的小伙伴可以先去学习一下,为了更直观,放弃了所有的封装,我已经写成最基础的使用方式了。。。vs_content就是ViewStub,initView是我封装了一层的代码,实际就是在onViewCreated()中调用的,当然,也可以在onCreateView中调用,这都看不懂的自行面壁。。。

  1. 第二种就比较复杂了,我就不上代码了,简单说就是根据需求,用多个ViewStub分功能区分,把界面拆成多个ViewStub分别维护,比如不同接口调用之后生成数据再填充的数据,在接口返回之后再去使用ViewStub的inflate方法,或者根据id单独inflate,这就涉及到ViewStub的使用了,这里就不做具体分析了。大家可以移步去看下其他大神们对应对应的文档。

总之,简单一句话,就是onCreateView的时候,尽量把当时就加载的View树整的越简单越好,这样做的目的就是防止在add或者replace占用过多的资源,导致App卡顿造成黑屏,同时ViewStub分功能加载也可以把一瞬间的性能要求分散到多点去,对整个App的开发都是有好处的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果在执行addFragment操作之后执行popBackStack操作,可能会导致Fragment重复add问题。为了避免该问题的发生,可以在执行addFragment操作时,先通过FragmentManager的findFragmentByTag方法判断当前Fragment是否已经被添加到Activity中,如果已经被添加,则不需要重复添加。 例如,下面的代码演示了如何在执行addFragment操作之前,先判断当前Fragment是否已经被添加到Activity中。如果已经被添加,则不需要重复添加;如果没有被添加,则执行addFragment操作: ```java // 获取FragmentManager FragmentManager fragmentManager = getSupportFragmentManager(); // 判断当前Fragment是否已经被添加到Activity中 MyFragment fragment = (MyFragment) fragmentManager.findFragmentByTag("MyFragment"); if (fragment == null) { // 如果没有被添加,则执行addFragment操作 fragment = new MyFragment(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.add(R.id.container, fragment, "MyFragment"); transaction.addToBackStack(null); transaction.commit(); } else { // 如果已经被添加,则不需要重复添加 fragmentManager.popBackStack(); } ``` 在上述代码中,我们首先通过findFragmentByTag方法判断当前Fragment是否已经被添加到Activity中。如果已经被添加,则不需要重复添加,直接执行popBackStack操作;如果没有被添加,则执行addFragment操作。 需要注意的是,这里使用了FragmentTransaction的add方法,并通过tag参数指定了Fragment的标签。在通过findFragmentByTag方法查找Fragment时,我们也需要指定相同的标签。这样可以确保我们找到的Fragment是我们想要的那个,避免出现错误。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值