3. Android交易页面架构
交易链路是Android应用中最复杂、状态最敏感、页面跳转最频繁的场景之一。从行情列表点击买入,到下单确认、支付验证、结果回执,再到返回持仓或订单详情,整条链路往往跨越5~8个页面,涉及登录态校验、风控拦截、超时重试、结果回传等多种分支。如果页面架构职责划分不清,就会出现"返回后数据错乱""重复下单""页面栈越堆越深"等典型问题。本节围绕Activity、Fragment、Compose与Navigation组件,梳理它们在交易链路中的职责边界与页面栈管理策略。
一、四类组件的职责边界
Activity是系统级容器与任务入口。在交易链路中,它承担三类不可替代的职责:一是作为任务栈的宿主,承载整个交易流程的窗口与生命周期;二是作为跨进程交互的锚点,例如拉起支付SDK、指纹/人脸验证、系统分享等需要Activity上下文的能力;三是作为深链与外部跳转的落点,从推送、H5或其它App跳入交易页时,必须有一个稳定的Activity作为入口。交易链路通常采用"单Activity + 多页面"或"少量Activity + 多Fragment/Compose页面"的架构,避免每个页面一个Activity导致任务栈碎片化。
Fragment是传统View体系下的页面单元,在交易链路中适合承载生命周期敏感、需要与宿主深度交互的页面,例如下单确认页需要监听返回键拦截、需要在onPause时保存草稿、需要与Activity共享ViewModel。Fragment的优势在于生命周期回调完整、与View系统耦合自然;劣势是嵌套层级深、状态恢复复杂。
Compose是声明式UI,在交易链路中适合承载状态驱动、UI随数据变化频繁重组的页面,例如行情列表、订单簿、下单表单的实时校验反馈。Compose页面本身不是页面栈的单元,它需要配合Navigation组件才能参与导航。Compose的优势是状态与UI绑定清晰、重组粒度细;劣势是与传统Fragment混用时生命周期语义需要额外对齐。
Navigation组件是页面栈的管理者,负责定义目的地(Destination)、动作(Action)、参数(Argument)与返回栈(Back Stack)。它统一了Fragment与Compose的导航模型,使交易链路的跳转关系可以集中声明、集中校验。
二、页面栈管理的核心策略
交易链路的页面栈管理,核心是回答三个问题:什么页面该入栈、什么页面该出栈、返回时数据如何回传。
第一,入栈策略。行情列表→下单确认→支付验证,这三步是典型的线性入栈。但要注意:下单确认页不应重复入栈。如果用户从行情页多次点击"买入",应通过launchSingleTop或popUpTo避免堆叠多个确认页。Navigation中可配置:
navController.navigate(
R.id.action_market_to_confirm,
bundleOf("symbol" to "BTC/USDT", "side" to "BUY"),
navOptions {
launchSingleTop = true
popUpTo(R.id.market_list) { inclusive = false }
}
)
第二,出栈策略。支付成功后,不应让用户按返回键回到支付验证页或下单确认页,否则可能触发重复下单。正确做法是:支付成功页入栈时,用popUpTo(R.id.order_confirm, inclusive = true)把确认页和验证页一起弹出,只保留行情列表作为返回落点。这样用户从结果页返回,直接回到行情列表,链路干净。
第三,数据回传。交易链路中,支付结果需要回传给下单确认页或订单详情页。传统做法是setFragmentResult或SavedStateHandle;Compose中则通过previousBackStackEntry.savedStateHandle写入结果,前一页面观察该状态。Navigation的SavedStateHandle是跨页面回传的推荐方式,它不依赖页面实例存活,进程重建后仍可恢复。
三、混合架构下的状态对齐
实际项目中,交易链路往往是Fragment与Compose混用的。例如下单确认页用Fragment承载(需要返回键拦截和草稿保存),支付验证页用Compose承载(需要实时状态反馈)。混用时,最大的风险是生命周期语义不对齐:Fragment的onPause与Compose的DisposableEffect释放时机不同,可能导致支付SDK回调时页面已销毁。
解决方式是:把交易链路的核心状态提升到Activity级别的ViewModel,Fragment和Compose页面都只做UI渲染与事件转发。ViewModel持有订单草稿、支付状态、风控结果等数据,页面销毁重建时从ViewModel恢复。这样,无论页面是Fragment还是Compose,状态都不依赖页面实例,页面栈的入栈出栈不会丢失交易上下文。
另一个关键点是返回键的统一处理。交易链路中,支付验证页的返回键应被拦截并弹出"确认放弃支付"对话框;下单确认页的返回键应保存草稿后再退出。在Fragment中通过OnBackPressedDispatcher注册回调;在Compose中通过BackHandler实现。两者都注册到同一个OnBackPressedDispatcher,由Navigation统一调度,避免多个页面同时拦截导致行为混乱。
四、一个可参考的职责划分表
| 页面/环节 | 承载组件 | 入栈/出栈策略 | 状态归属 |
|---|---|---|---|
| 行情列表 | Compose | 栈底,常驻 | MarketViewModel |
| 下单确认 | Fragment | launchSingleTop,防重复入栈 | TradeViewModel(Activity级) |
| 支付验证 | Compose | 入栈时popUpTo确认页 | TradeViewModel + PayState |
| 支付结果 | Compose | popUpTo确认页inclusive,返回落点为行情列表 | TradeViewModel |
| 订单详情 | Fragment | 独立子图,可从结果页或持仓页进入 | OrderViewModel |
这张表的核心思想是:组件选型服务于页面特性,页面栈策略服务于交易安全,状态归属服务于进程重建。三者对齐后,交易链路才能在频繁跳转、超时重试、进程被杀等异常场景下保持数据一致与操作安全。
LocalContext和rememberLauncherForActivityResult桥接,桥接层越薄越稳定。总结来说,Activity是交易链路的"地基",负责窗口、任务栈与系统能力锚点;Fragment是"承重墙",承载生命周期敏感、交互复杂的页面;Compose是"轻质隔断",适合状态驱动、快速迭代的页面;Navigation是"施工图",统一声明页面关系与返回栈规则。四者各司其职,交易链路才能在复杂业务与异常场景下保持稳定。