Conductor源码分析

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

关键步骤说明

  1. 发起导航:ControllerA通过router.pushController()发起导航请求
  2. BackStack操作:Router将ControllerB的Transaction压入Backstack
  3. 设置Router:为ControllerB设置Router引用,触发onContextAvailable()
  4. 创建View:调用ControllerB的inflate()方法,内部调用onCreateView()创建View
  5. 生命周期通知
    • ControllerB触发changeStarted(PUSH_ENTER)onChangeStarted()
    • ControllerA触发changeStarted(PUSH_EXIT)onChangeStarted()
  6. 执行切换:ControllerChangeHandler执行performChange(),将ViewB添加到容器并执行动画
  7. 完成回调:动画完成后触发onChangeCompleted(),通知两个Controller的changeEnded()
  8. 附加到窗口:ViewB附加到窗口后触发onAttach(),ControllerB进入活跃状态

注意事项

  • ControllerA在切换过程中不会被销毁,只是View被隐藏,仍保留在Backstack中
  • 如果用户按返回键,会执行相反的pop流程,ControllerA会重新显示
  • 整个流程都在主线程执行,确保UI操作的线程安全
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容