ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

B08_Fragment事务通信与双生命周期

B08_Fragment事务通信与双生命周期 Android 基础补强 B08Fragment 还在为什么 binding 已经不能用了摘要用列表进入详情再返回的过程理解 Fragment 事务、返回栈、View 生命周期及结果通信避免对旧 View 更新和重复添加页面。标签Android、Fragment、生命周期、ViewBinding、第一行代码本文对应《第一行代码》第 3 版第 5 章以及 28 天课程 D17 的传统界面练习。Fragment 是受宿主管理的界面组件但 Fragment 对象存在和它的 View 存在不是同一件事。下文使用独立的 FragmentActivity 练习入口不改主项目的 Navigation 3代码片段需要补齐布局、依赖和工厂尚未编译。1. 用返回操作理解两段寿命列表 Fragment A 进入详情 B如果事务允许返回A 可能仍由 FragmentManager 保留而它之前创建的 View 已被销毁。返回时 A 可以重新创建 View。于是成员变量_binding若一直持有旧 Binding就等于持续引用旧界面树异步回调还可能更新一份已经不显示的控件。Fragment 生命周期描述组件本身View 生命周期描述本次界面实例。访问 Binding 应限制在 View 创建之后、销毁之前。页面的业务数据可以放在 ViewModel 或 Repository控件引用不能跟随较长寿命的业务对象一起保存。Fragment 生命周期这也解释了为什么仅把 Binding 声明成可空还不够。需要在销毁时释放引用让收集界面状态的任务跟随viewLifecycleOwner并避免长期对象保留 View、Activity 或带着它们的闭包。空安全和对象寿命是两类问题。2. 一次 View 创建对应一次收集关系假设fragment_articles.xml生成FragmentArticlesBinding包含列表articleListarticleAdapter来自 B07viewModel.state发出不可变列表状态。以下代码位于 Fragment 内依赖 Fragment、Lifecycle、RecyclerView 与 ViewBinding。privatevarbinding:FragmentArticlesBinding?nulloverridefunonCreateView(inflater:LayoutInflater,container:ViewGroup?,savedInstanceState:Bundle?):View{valcurrentFragmentArticlesBinding.inflate(inflater,container,false)bindingcurrentreturncurrent.root}overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super.onViewCreated(view,savedInstanceState)valcurrentcheckNotNull(binding)current.articleList.layoutManagerLinearLayoutManager(requireContext())current.articleList.adapterarticleAdapter viewLifecycleOwner.lifecycleScope.launch{viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED){viewModel.state.collect{articleAdapter.submitList(it.items)}}}}overridefunonDestroyView(){binding?.articleList?.adapternullbindingnullsuper.onDestroyView()}生命周期低于指定状态时内部收集停止重新达到状态时再启动。重新收集不应自动等同于重复发送网络请求是否刷新由数据层策略决定。上例把 Adapter 从旧 RecyclerView 脱离也让引用边界更清晰如果 Adapter 本身长期持有旧 Activity仍需要修复清一个字段不是万能证明。3. 事务与返回栈需要显式说明Activity 首次创建练习入口时只在savedInstanceState null时添加初始 Fragment因为恢复时 FragmentManager 可以恢复已有组件。无条件重复添加可能导致旋转后出现重叠或重复实例。进入详情用事务替换容器若期望返回列表要加入返回栈。下面假设宿主布局包含FragmentContainerViewID 为fragment_container使用 Fragment KTX 的类形式操作。代码放在有效的用户交互时刻不能拿它解决宿主已经保存状态后的任意异步导航。supportFragmentManager.commit{setReorderingAllowed(true)replaceArticleDetailFragment(R.id.fragment_container,argsbundleOf(articleIdtoselectedId))addToBackStack(article_detail)}commit安排事务稍后执行不意味着下一行就能找到新 View。commitNow是另一种即时提交方式并不能与加入返回栈随意混用。遇到状态已保存后的提交问题应检查触发时机与恢复策略而不是默认改成允许状态丢失。Fragment 事务说明传入文章 ID 让详情按身份查询数据避免把可变的大对象与控件引用塞进参数。无效 ID 应进入明确的不可用页面这样恢复过程和正常点击过程可以使用相同入口。4. 通信先区分持续状态与一次结果列表和详情共享收藏状态适合读取同一个 Repository 或合适作用域的 ViewModel。详情不必持有列表 Fragment然后直接寻找列表按钮改色。对于“选择完一个筛选项返回”的一次结果可以使用 Fragment Result API发送方和监听方必须使用同一个 FragmentManager 和同一个键。Fragment 通信指南监听对象达到可交付的生命周期状态后才处理结果。结果键并不是持久消息队列同一键在尚未交付时重复设置可能只保留最新结果。因此不要用它保存整个收藏历史也不要把“发送过一次”当作数据库已经持久化。共享 ViewModel 同样需要明确作用域。Activity 级共享可能适合两个同宿主页面但范围过大也会让无关页面共享状态。选择范围时应回答“谁需要共同读写这份状态、什么时候应该结束”而不是只为了方便调用。5. 进入、返回、旋转的实验顺序给 Fragment 和每次创建的 root 记录不同实例标识。列表进入详情再返回预期能观察到组件与 View 寿命并非总是同步旧 View 的收集在销毁后结束新 View 有自己的收集。重复十次检查是否出现重复观察和旧对象更新日志。随后在列表旋转设备确认不会重复添加初始 Fragment。最后从详情修改收藏再返回预期列表通过共享数据源显示最新状态而不是依赖保存下来的 View 引用。若结果不一致先查询数据再检查作用域及收集生命周期。以上为实验预期实际日志需要自行执行后补充。6. 原创面试问答与追问问一Fragment 没销毁为什么要清 Binding它的 View 可能已销毁Binding 引用的是那次 View。追问只用 Fragment 的生命周期收集够吗界面更新应绑定 View 的生命周期。问二commit 之后能立即操作新控件吗不能假定事务已经执行。追问是否统一换 commitNow不应返回栈与时机约束不同应在组件正常 View 回调中初始化。问三Fragment Result 和共享 ViewModel 怎么选一次结果与持续共享状态需求不同。追问收藏归属哪个长期事实应由数据层持久化共享状态持有者组织展示。问答为原创整理并非面试鸭题库原题。验收时能画出一条进入、返回与 View 重建时间线解释引用何时有效、事务怎样恢复、数据如何共享就完成了本篇第 5 章基础补强。
返回列表