Conductor源码分析
1. 项目的核心功能
Conductor是一个轻量级却功能强大的Android View管理框架。它主要用于:
- 支持基于View的单Activity应用开发,而无需使用Fragment。
- 提供简洁但强大的生命周期管理。
- 管理导航和BackStack(可回退栈)。
- 支持View切换动画和状态保存。
- 能响应onActivityResult、onRequestPermissionsResult等回调。
- 适配多种建设性架构(MVP/MVVM/MVI等)。
2. 项目的核心原理
Conductor核心思想是用Controller类替代传统的Fragment,配合Router实现页面导航和管理。
- 每个页面(UI片段)由一个
Controller负责,类似Fragment,但更轻量。 -
Router绑定到Activity和ViewGroup实例上,负责Controller的增删、切换和BackStack管理。 - 所有页面跳转(push/pop等导航操作)实际是对BackStack的操作。
- 切换时,可插拔自定义的
ControllerChangeHandler以实现动画。
3. 核心类作用分析
Controller
- 表示一个独立的UI单元,负责管理自己的View、生命周期。
- 生命周期方法包括:onCreateView、onAttach、onDetach、onDestroyView、onDestroy等。
- 类似Fragment但无复杂耦合,且完全基于View实现。
Router
- 负责在指定的ViewGroup内管理Controller的增删、跳转和回退栈。
- 对应Activity/Container一一对应,管理Backstack的操作:pushController、popController等。
- 通过
ControllerChangeHandler支持页面切换动画与过渡。 - 支持子Router,便于复杂嵌套结构(如Master-Detail或ViewPager)。
RouterTransaction
- 描述一次Controller添加到Router的事务,包含目标Controller及切换动画等参数。
Backstack
- 本质是一个
Deque,维护Router当前Controller的栈结构,实现push、pop等操作。
4. 路由原理分析
- Router管理BackStack,每个Router挂载在Activity/ViewGroup上。
- 典型导航流程:
- 通过
pushController(RouterTransaction)往栈顶压入Controller,显示新页面。 - 通过
popController()或handleBack()弹出栈顶Controller,回退到上一个页面。 - 切换时触发
ControllerChangeHandler实现动画。
- 通过
- Router支持
popToRoot()和按tag、实例id精准回退。 - 支持根Router及子Router(通过Controller的getChildRouter实现)。
- 状态可序列化保存,支持配置变更和进程重启恢复。
5. 关键流程的UML流程图
下面是Controller生命周期的UML流程图(官方文档提供):
[图片上传失败...(image-26da21-1765039383784)]
核心流程说明:
- Controller实例创建 → onCreateView(渲染UI)→ onAttach(附加到窗口)→ in focus(可交互)
- 离开焦点时onDetach,View被销毁触发onDestroyView,整个Controller销毁触发onDestroy
- App进程被杀时能恢复状态
如需详细导航流程、动画切换等状态机流程可进一步绘制。
6. 从A页面打开B页面的核心代码UML调用时序
下面是当从ControllerA打开ControllerB时的完整调用时序图:
participant A as ControllerA
participant Router as Router
participant Backstack as Backstack
participant Handler as ControllerChangeHandler
participant B as ControllerB
participant ViewB as ViewB
Note over A,ViewB: 用户触发导航:从A页面打开B页面
A->>Router: router.pushController(RouterTransaction.with(ControllerB))
activate Router
Router->>Router: ThreadUtils.ensureMainThread()
Router->>Backstack: backstack.peek() (获取当前栈顶ControllerA)
Backstack-->>Router: fromTransaction (ControllerA的Transaction)
Router->>Router: pushToBackstack(transaction)
Router->>Backstack: backstack.push(transaction)
Backstack-->>Router: (ControllerB已入栈)
Router->>Router: performControllerChange(transaction, from, true)
Router->>Router: to.onAttachedToRouter()
Router->>Router: setRouterOnController(toController)
Router->>B: controller.setRouter(this)
Router->>B: controller.onContextAvailable()
B->>B: onContextAvailable(context)
Router->>Router: performControllerChange(to, from, isPush, changeHandler)
Router->>Router: 创建ChangeTransaction
Router->>Handler: ControllerChangeHandler.executeChange(transaction)
activate Handler
Note over Handler: 执行切换逻辑
Handler->>Handler: 获取或创建ControllerChangeHandler实例
Handler->>Handler: handler.hasBeenUsed = true
alt from != null (存在A页面)
Handler->>Handler: completeHandlerImmediately(from.getInstanceId())
end
Handler->>Handler: inProgressChangeHandlers[to.getInstanceId()] = handler
Handler->>Router: listeners.forEach { onChangeStarted(...) }
Router-->>Handler: (通知所有监听器)
Note over B: 开始创建B的View
Handler->>B: to.inflate(container)
activate B
B->>B: onCreateView(inflater, container, savedViewState)
B-->>Handler: view (B的View对象)
Handler->>B: to.changeStarted(handler, PUSH_ENTER)
B->>B: onChangeStarted(handler, PUSH_ENTER)
deactivate B
Handler->>A: from.getView()
A-->>Handler: viewA (A的View对象)
Handler->>A: from.changeStarted(handler, PUSH_EXIT)
A->>A: onChangeStarted(handler, PUSH_EXIT)
A->>A: isPerformingExitTransition = true
Note over Handler,ViewB: 执行View切换动画
Handler->>Handler: handler.performChange(container, fromView, toView, isPush, changeListener)
Handler->>ViewB: 将ViewB添加到container (执行动画)
activate ViewB
Note over ViewB: 动画执行中...
ViewB-->>Handler: onChangeCompleted() (动画完成回调)
deactivate ViewB
Handler->>A: from.changeEnded(handler, PUSH_EXIT)
A->>A: onChangeEnded(handler, PUSH_EXIT)
A->>A: isPerformingExitTransition = false
Handler->>B: to.changeEnded(handler, PUSH_ENTER)
B->>B: onChangeEnded(handler, PUSH_ENTER)
Handler->>Router: listeners.forEach { onChangeCompleted(...) }
Router-->>Handler: (通知所有监听器切换完成)
Handler->>Handler: inProgressChangeHandlers.remove(to.getInstanceId())
alt handler.removesFromViewOnPush
Handler->>A: from.needsAttach = false
end
deactivate Handler
Note over B: ViewB附加到窗口后触发
ViewB->>B: ViewAttachHandler.onAttached()
B->>B: attach(view)
B->>B: attached = true
B->>B: onAttach(view)
Note over A,B: B页面已显示,A页面已隐藏(但仍在Backstack中)
deactivate Router
关键步骤说明:
-
发起导航:ControllerA通过
router.pushController()发起导航请求 - BackStack操作:Router将ControllerB的Transaction压入Backstack
-
设置Router:为ControllerB设置Router引用,触发
onContextAvailable() -
创建View:调用ControllerB的
inflate()方法,内部调用onCreateView()创建View -
生命周期通知:
- ControllerB触发
changeStarted(PUSH_ENTER)和onChangeStarted() - ControllerA触发
changeStarted(PUSH_EXIT)和onChangeStarted()
- ControllerB触发
-
执行切换:ControllerChangeHandler执行
performChange(),将ViewB添加到容器并执行动画 -
完成回调:动画完成后触发
onChangeCompleted(),通知两个Controller的changeEnded() -
附加到窗口:ViewB附加到窗口后触发
onAttach(),ControllerB进入活跃状态
注意事项:
- ControllerA在切换过程中不会被销毁,只是View被隐藏,仍保留在Backstack中
- 如果用户按返回键,会执行相反的pop流程,ControllerA会重新显示
- 整个流程都在主线程执行,确保UI操作的线程安全