Fragment使用中的优化

fragment的启动优化

我们先来看一段min栏的启动代码。

supportFragmentManager.beginTransaction()
            .add(R.id.mini_player, MiniPlayFragment().apply {
            })
            .commitAllowingStateLoss()

xml:

<FrameLayout
    android:id="@+id/mini_player"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

出现的警告信息。

06-19 16:37:18.946 17397 17397 D FragmentManager: StrictMode violation in com.flyme.auto.localmusic.ui.main.fragments.MiniPlayerFragment
06-19 16:37:18.946 17397 17397 D FragmentManager: androidx.fragment.app.strictmode.WrongFragmentContainerViolation: Attempting to add fragment MiniPlayerFragment{706b678} (b26a8c70-04f6-40b3-ac6f-41e4ea805151 id=0x7f0a01f4) to container android.widget.FrameLayout{8ebca9b V.E...... ......I. 0,0-0,0 #7f0a01f4 app:id/mini_player_container} which is not a FragmentContainerView
06-19 16:37:18.946 17397 17397 D FragmentManager: 	at androidx.fragment.app.strictmode.FragmentStrictMode.i(FragmentStrictMode.kt:13)
06-19 16:37:18.946 17397 17397 D FragmentManager: 	at androidx.fragment.app.h.f(FragmentStateManager.java:158)
06-19 16:37:18.946 17397 17397 D FragmentManager: 	at androidx.fragment.app.h.m(FragmentStateManager.java:125)
06-19 16:37:18.946 17397 17397 D FragmentManager: 	at androidx.fragment.app.i.t(FragmentStore.java:31)
06-19 16:37:18.946 17397 17397 D FragmentManager: 	at androidx.fragment.app.FragmentManager.W0(FragmentManager.java:28)
06-19 16:37:18.946 17397 17397 D FragmentManager: 	at androidx.fragment.app.FragmentManager.S(FragmentManager.java:10)
06-19 16:37:18.946 17397 17397 D FragmentManager: 	at androidx.fragment.app.FragmentManager.x(FragmentManager.java:12)
  • 主要问题
    WrongFragmentContainerViolation:尝试将 MiniPlayerFragment 添加到一个不是 FragmentContainerView 的容器(android.widget.FrameLayout)中。

可优化的点

1、 修改为FragmentContainerView

这是 AndroidX Fragment 库的严格模式(StrictMode)检测到的违规行为。

从 AndroidX Fragment 1.3.0 开始,推荐使用 FragmentContainerView 作为 Fragment 的容器,而不是普通的 FrameLayout。

1.1 设计目的
  • FragmentContainerView

专门为 Fragment 设计,继承自 FrameLayout,但针对 Fragment 的使用场景做了优化。
是 AndroidX Fragment 库(1.2.0+)推荐的容器,未来会支持更多 Fragment 专属特性。

  • FrameLayout

通用的视图容器,并非为 Fragment 定制,可能存在一些边缘场景的兼容性问题。

1.2 主要区别
特性FragmentContainerViewFrameLayout
动画支持完美支持 Fragment 转场动画(如 enter/exit)可能因视图层级问题导致动画异常
生命周期同步严格保证 Fragment 和容器视图的生命周期同步可能出现短暂的生命周期不一致。
Z-Order 处理自动管理 Fragment 的视图层级(避免重叠问题)需要手动控制视图层级
严格模式检查通过 StrictMode 检查(默认要求)会触发 WrongFragmentContainerViolation 警告
Fragment 事务优化对 add/replace 等操作有内部优化无特殊优化

2、 commitAllowingStateLoss()

这里使用了commitAllowingStateLoss,但不建议这样子使用

  • 状态安全性:MiniPlayerFragment 可能是 UI 核心组件,状态丢失会导致用户体验问题。
  • 替代方案:

使用 commit() + 检查 isStateSaved:

if (!supportFragmentManager.isStateSaved) {
    supportFragmentManager.commit { add(...) }
}
1. 核心问题:commit() 的时机限制

当 Activity 或 Fragment 正在销毁(如屏幕旋转、返回键退出、内存回收)时,其状态会被保存(调用 onSaveInstanceState),此时:

  • commit():
    如果在 状态已保存后 调用,会直接抛出异常:
IllegalStateException: Can not perform this action after onSaveInstanceState
  • commitAllowingStateLoss():
    允许提交,但可能导致 UI 状态丢失(如 Fragment 添加了但视图不显示)。
2. isStateSaved 的作用

FragmentManager.isStateSaved 是一个状态检查方法,用于判断当前是否允许安全提交事务:

  • 返回 true:
    Activity/Fragment 已触发 onSaveInstanceState,此时提交事务可能失败或丢失状态。
  • 返回 false:
    状态未保存,可以安全调用 commit()。
3. 典型场景

(1) 异步回调中提交事务
例如在网络请求回调或 LiveData 观察中动态添加 Fragment:

viewModel.data.observe(this) { result ->
    // 检查状态是否已保存
    if (!supportFragmentManager.isStateSaved) {
        supportFragmentManager.commit {
            replace(R.id.container, ResultFragment(result))
        }
    }
}

(2) 处理用户快速操作
当用户快速点击按钮触发多个 Fragment 事务时,防止因 Activity 突然进入后台导致崩溃:

button.setOnClickListener {
    if (!supportFragmentManager.isStateSaved) {
        supportFragmentManager.commit { 
            add(R.id.container, DialogFragment())
        }
    }
}

(3) 替代 commitAllowingStateLoss()
避免滥用 commitAllowingStateLoss()(状态丢失风险),改用安全提交:

fun safeCommit(block: FragmentTransaction.() -> Unit) {
    if (!supportFragmentManager.isStateSaved) {
        supportFragmentManager.commit(block)
    } // 否则忽略或记录日志
}
4. 为什么需要这种检查?
  • 状态一致性:
    Android 的 onSaveInstanceState 机制要求 UI 状态在此时必须固定,后续修改无法被保存,可能导致恢复时界面错乱。

  • 崩溃预防:
    直接调用 commit() 在状态保存后会立即抛出异常,而 isStateSaved 提供了前置检查机会。

5. 对比 commitAllowingStateLoss()

方法安全性状态丢失风险适用场景
commit() + isStateSaved绝大多数需要安全提交的场景
commitAllowingStateLoss()非关键UI更新(如日志记录)

6. 常见误区

  • 误区 1:认为 commitAllowingStateLoss() 是万能解决方案。
    事实:它只是隐藏了问题,可能导致用户看到残缺的 UI 状态(如 Fragment 已添加但视图未显示)。

  • 误区 2:在 onCreate 中无需检查 isStateSaved。
    事实:onCreate 通常是安全的,但若从后台恢复时执行异步操作仍需检查。

总结
  • commit() + isStateSaved 是处理 Fragment 事务提交时机的 安全模式,优先于 commitAllowingStateLoss。

  • 适用场景:所有动态提交 Fragment 事务的场合(尤其是异步回调、用户交互等不确定时机)。

  • 本质:通过前置检查避免 IllegalStateException 和状态丢失,兼顾稳定性和用户体验。

3、 setReorderingAllowed

setReorderingAllowed(true) 是 Fragment 事务(FragmentTransaction)中的一个优化选项,主要用于 提升 Fragment 事务的执行效率 和 避免不必要的中间状态。以下是它的详细解释和实际作用:

1. 什么是事务优化?

当执行多个 Fragment 操作(如 add、replace、remove)时,默认情况下,FragmentManager 会按顺序逐步执行每个操作,导致 中间状态被频繁触发(例如短暂的生命周期变化或视图重绘)。
启用 setReorderingAllowed(true) 后,FragmentManager 会将这些操作合并为一个 原子性变更,跳过中间状态,直接呈现最终结果。

2. 解决了什么问题?

(1) 性能提升

  • 减少不必要的生命周期回调:
    例如,替换 Fragment A → B 时,默认会先执行 A 的 onPause → onStop,再执行 B 的 onStart → onResume。启用优化后,可能跳过部分中间状态。
  • 减少视图重绘次数:
    避免因多次操作导致的界面闪烁。

(2) 避免动画冲突
在转场动画(如 setCustomAnimations)中,优化后的动画会更流畅,避免因中间状态导致的动画断裂。

(3) 防止状态不一致
在复杂事务(如同时执行 add + remove + hide)中,确保所有操作一次性完成,降低并发问题风险。

3. 适用场景
  • 多个 Fragment 同时操作(例如 replace + addToBackStack)。
  • 使用转场动画(需要平滑过渡效果时)。
    嵌套 Fragment 或动态场景(如 ViewPager2 配合 Fragment)。
4. 底层原理
  • 操作合并:
    将多个 add/remove/replace 操作合并为一次视图层级变更。

  • 生命周期跳过:
    通过 FragmentStateManager 直接跳转到最终状态(如从 CREATED 到 RESUMED,跳过 STARTED)。

  • 动画优化:
    使用 SpecialEffectsController 统一处理动画队列。

5. 代码示例对比
supportFragmentManager.commit {
    replace(R.id.container, FragmentA())
    addToBackStack(null)
    setReorderingAllowed(true) // 合并操作为原子性变更
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值