Router — 一种架构模式,集中管理应用程序屏幕之间的导航逻辑。Router 决定事件发生时导航到哪里以及如何导航。在 iOS 中,Router 抽象了 UINavigationController 和 segue,在 Android 中 — FragmentManager 和 Jetpack Navigation 的 NavController。Router 可以作为独立模式运行,也可以作为 Coordinator(Router-per-Coordinator)和 VIPER(Router-per-module)的组件。更多信息 — 参见 Android Navigation Component Guide。
要点
Router — 封装屏幕间转换逻辑的模式。核心理念:ViewController(或 Presenter/ViewModel)不直接调用 navigationController.pushViewController,而是通知 Router 其导航意图。Router 决定创建哪个 ViewController 以及如何显示它。Router 可以简单(一组导航方法)或复杂(基于 URL 方案、deep link 和推送通知的路由)。
三种 Router 方法 — Router 作为服务(全局 Navigator 单例)、Router 作为模块组件(VIPER 方法)、Router 作为协议(Coordinator 的抽象)。全局 Router — 简单性(每个应用程序一个实例)— 在小型项目中流行。模块化 Router — 每个功能都有自己的 Router — VIPER 和 Clean Architecture 的标准。协议化 Router — 用于 Coordinator:带有 push/present/pop/dismiss 方法的 RouterProtocol。
| 方法 | 结构 | 使用时机 |
|---|---|---|
| 全局 Navigator | 带有转换方法的单例 | 小型项目、原型 |
| 模块化 Router | 每个模块内部的 Router | VIPER、Clean Architecture |
| 协议化 Router | 协议 + Coordinator 的实现 | MVVM-C、Coordinator 模式 |
模式历史 — Router 出现在 Web 框架中(URL Router — Rails Routes、Express.js),并被适配用于移动开发。在 iOS 中,Router 随着 VIPER 的兴起(2015-2017)而流行。在 Android 中,Router 是 Jetpack Navigation(2018)的一部分,这是 Google 的导航标准解决方案。现在 Router 是每个模块化移动应用架构不可或缺的一部分。
iOS Router — 包含不同类型导航方法的协议。基本实现包装了 UINavigationController。Router 可以管理模态呈现(present/dismiss)、导航栈中的 push/pop、Split View Controller 的 showDetail 以及全屏呈现(iOS 13+)。Router 不创建 ViewController — 它接收一个准备好的实例并显示它。ViewController 的创建由工厂(Assembly、DI 容器)或 Coordinator 完成。
// 针对 iOS 的 RouterProtocol
protocol RouterProtocol {
func push(_ viewController: UIViewController, animated: Bool)
func pop(animated: Bool)
func popToRoot(animated: Bool)
func present(_ viewController: UIViewController, animated: Bool)
func dismiss(animated: Bool)
func setViewControllers(_: [UIViewController], animated: Bool)
}
// 通过 UINavigationController 实现 Router
class NavigationRouter: RouterProtocol {
private let navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func push(_ vc: UIViewController, animated: Bool) {
if navigationController.presentedViewController {
navigationController.dismiss(animated: false)
}
navigationController.pushViewController(vc, animated: animated)
}
func pop(animated: Bool) {
navigationController.popViewController(animated: animated)
}
func popToRoot(animated: Bool) {
navigationController.popToRootViewController(animated: animated)
}
func present(_ vc: UIViewController, animated: Bool) {
navigationController.present(vc, animated: animated)
}
func dismiss(animated: Bool) {
navigationController.dismiss(animated: animated)
}
func setViewControllers(_ vcs: [UIViewController], animated: Bool) {
navigationController.setViewControllers(vcs, animated: animated)
}
}
// Assembly — 创建带 Router 的模块
protocol ProfileModuleFactory {
func makeProfileView() -> UIViewController
}
VIPER 中的 Router — 每个 VIPER 模块都有自己的 Router,知道应从当前模块打开哪些屏幕。Router 通过工厂创建下一个模块的 ViewController 并转移控制权。VIPER 中的 Router — 唯一包含 import UIKit 的组件(除了 View)。这简化了 Presenter 和 Interactor 的测试:它们不依赖于 UIKit。
Jetpack Navigation Component — Android 的标准路由器,自 2018 年起由 Google 推荐。导航在 XML 图(nav_graph.xml)中定义:节点(fragments、activities、destinations)和边(actions)。NavController 管理转换:调用 FragmentManager.replace/add 并管理返回栈。Navigation Component 支持 deep link、参数、转换动画以及 Safe Args 实现类型安全的数据传输。
// nav_graph.xml — 导航图
<!-- @xml/nav_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/loginFragment">
<fragment android:id="@+id/loginFragment"
android:name=".ui.login.LoginFragment">
<action android:id="@+id/to_home"
app:destination="@id/homeFragment" />
</fragment>
<fragment android:id="@+id/homeFragment"
android:name=".ui.home.HomeFragment">
<argument android:name="userId"
android:defaultValue="0"
app:argType="integer" />
<deepLink app:uri="myapp://home/{userId}" />
</fragment>
</navigation>
// Kotlin — 通过 NavController 导航
class LoginFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.loginButton.setOnClickListener {
val action = LoginFragmentDirections.toHome(userId = 42)
findNavController().navigate(action)
}
}
}
// Safe Args — 类型安全参数
class HomeFragmentArgs : NavArgs {
val userId: Int get() = arguments?.getInt("userId") ?: 0
}
// NavigationUI — 与 Toolbar、BottomNav、Drawer 的集成
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHost = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHost.navController
NavigationUI.setupActionBarWithNavController(this, navController)
}
}
Android Navigation 中的 Deep Links — Navigation Component 支持显式 deep link(PendingIntent + NavDeepLinkBuilder)和隐式 deep link(通过清单中的 intent-filter)。Deep link 可以指向 nav_graph 中的任何节点。推送通知的处理:Notification 使用 NavDeepLinkBuilder 创建 PendingIntent,恢复返回栈并打开所需屏幕。Navigation Component 自动处理 Up/Back 按钮并将用户返回到上一个屏幕。
URL Routing — 基于 URL 方案、deep link 和 universal link 的路由。应用程序注册一个方案(myapp://profile/42)或 universal link(https://example.com/profile/42)。Router 解析 URL,提取参数(userId=42)并打开相应的屏幕。URL Routing 允许从外部打开应用程序:通过网站链接、电子邮件、推送通知、二维码。iOS 使用 NSUserActivity + universal link,Android — intent-filter + deep link。
// URL Router — 解析 deep link 和导航
protocol URLRoute {
var pattern: String { get }
func navigate(parameters: [String: String], router: RouterProtocol)
}
struct ProfileRoute: URLRoute {
let pattern = "myapp://profile/{userId}"
func navigate(parameters: [String: String], router: RouterProtocol) {
guard let userId = parameters["userId"] else { return }
let profileVC = ProfileViewController(userId: userId)
router.push(profileVC, animated: true)
}
}
// URL Router — 将 URL 映射到路由
class URLRouter {
private var routes: [URLRoute] = []
func handle(_ url: URL, router: RouterProtocol) {
for route in routes {
if let params = matchPattern(route.pattern, url: url) {
route.navigate(parameters: params, router: router)
return
}
}
}
}
Deep link 路由实践 — 应用程序必须在三种状态下处理 deep link:未启动(通过 deep link 启动)、后台(从后台恢复)、活跃(onNewIntent/SceneDelegate)。URL Router 必须正确恢复返回栈:当 deep link 到个人资料屏幕时,用户应该能够按 Back 并返回到上一个屏幕。iOS 和 Android 对返回栈的处理不同 — Router 必须考虑平台特性。
Router vs Coordinator — Router 回答“如何显示屏幕?”(push/present),Coordinator — “显示哪个屏幕?”(启动哪个流程)。Router — 导航工具,Coordinator — 流程组织者。Router 由 Coordinator 使用。在 VIPER 中,Router 执行两种功能:决定导航到哪里以及如何显示。在 MVVM-C 中,Router 是 Coordinator 使用的独立协议。这些模式并不互斥,而是相互补充。
| 特性 | Router | Coordinator | Navigator |
|---|---|---|---|
| 焦点 | 导航机制 | 导航流程 | 全局访问 |
| 层级 | UI 框架(push/present) | 业务逻辑(流程) | 基础设施 |
| 屏幕创建 | 接收准备好的 VC | 通过工厂创建 VC | 创建 VC |
| 生命周期 | 每个导航一个 | childCoordinators | 单例 |
Router vs Navigator — Navigator 是 iOS 社区中 Router 的旧称。在早期实现中,Navigator 是一个带有转换方法的单例类。Navigator 是全局的 Router 单例。Navigator 更简单,但会产生隐藏的依赖关系。基于协议的 Router 更可测试且更模块化。Navigator 适用于没有严格架构的项目,Router 适用于 Clean Architecture 和 VIPER。
常见问题
Router — 对 UINavigationController 的抽象(push/present/pop)。Coordinator — Router 之上的层,管理流程(接下来显示哪个屏幕)。Router 负责机制,Coordinator 负责导航逻辑。Coordinator 使用 Router 执行转换。在 VIPER 中,Router 结合了这两种功能,而在 MVVM-C 中它们是分开的。
不。对于有 3-5 个屏幕的应用程序,直接调用 navigationController.pushViewController 是正常解决方案。Router 在 10+ 个屏幕、deep link、推送通知和模块化架构时变得有用。Router 简化了导航测试,并允许集中处理来自任何来源(按钮、deep link、推送)的转换。
URL Router 是 Router 的扩展,用于解析 deep link 和 universal link。URL Router 确定 URL 与应用程序屏幕之间的对应关系。例如,route myapp://profile/42 → ProfileViewController(userId: 42)。URL Router 集中处理来自不同来源的 deep link:推送通知、二维码、电子邮件链接、通用链接。
可以。Router 可以独立运行 — 作为全局 Navigator 或作为 VIPER 模块的组件。没有 Coordinator 的 Router — 只是将 UINavigationController 封装在协议之后。没有 Router 的 Coordinator — 更困难(ViewController 自己调用 push/present),但可行。最佳实践 — Router + Coordinator 一起使用。
功能上 — 是的,两者都集中管理导航。实现不同:iOS Router — 协议 + UINavigationController 周围的类;Android — Jetpack Navigation Component 带有 XML 图和 NavController。Android Router 内置于框架(Navigation Component)中,iOS Router 是外部实现。Android Router 支持 Safe Args,iOS Router 不支持(需要泛型或 Codable)。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。