Skip to content

模块化架构

字数统计:约 10000 字
难度等级:⭐⭐⭐⭐
面试重要度:⭐⭐⭐⭐⭐


目录

  1. 模块化简介
  2. 模块化设计原则
  3. Android 模块化实践
  4. 模块间通信
  5. 依赖管理
  6. 面试考点

1. 模块化简介

1.1 什么是模块化

模块化是将应用拆分为独立模块的过程:
- 每个模块有明确职责
- 模块间低耦合
- 可独立编译和测试
- 便于团队协作

1.2 模块化的好处

好处:
- 编译速度提升(并行编译)
- 代码复用(跨项目共享)
- 团队独立开发
- 按需加载(Dynamic Feature)
- 更好的代码组织
- 便于测试

挑战:
- 模块间通信复杂
- 依赖管理复杂
- 初始配置成本高

1.3 模块化架构模式

常见架构:
┌─────────────────────────────────────┐
│              app                    │  ← 应用模块(组装)
├─────────────────────────────────────┤
│   feature:login  │  feature:home   │  ← 功能模块
├─────────────────────────────────────┤
│  core:network   │  core:database   │  ← 核心模块
├─────────────────────────────────────┤
│      common      │     utils        │  ← 基础模块
└─────────────────────────────────────┘

2. 模块化设计原则

2.1 SOLID 原则

kotlin
// 1. 单一职责原则 (SRP)
// 每个模块只做一件事
// core:network - 只负责网络
// core:database - 只负责数据库

// 2. 开闭原则 (OCP)
// 对扩展开放,对修改关闭
interface UserRepository {
    suspend fun getUser(id: String): User
}

// 扩展时添加新实现,不修改接口
class FirebaseUserRepository : UserRepository {
    override suspend fun getUser(id: String): User {
        // Firebase 实现
    }
}

// 3. 依赖倒置原则 (DIP)
// 依赖抽象,不依赖具体实现
class LoginViewModel(
    private val userRepository: UserRepository // 依赖抽象
) {
    // ...
}

2.2 模块分层

推荐分层:
┌─────────────────────────────────────┐
│              app                    │  ← 应用层(组装)
├─────────────────────────────────────┤
│         feature:* (功能模块)         │  ← 业务层
├─────────────────────────────────────┤
│         core:* (核心模块)            │  ← 数据层
├─────────────────────────────────────┤
│         common (基础模块)            │  ← 基础层
└─────────────────────────────────────┘

依赖方向:
app → feature → core → common

2.3 模块职责

kotlin
// app 模块
// 职责:组装应用,配置入口
// 依赖:所有 feature 模块

// feature 模块
// 职责:实现具体业务功能
// 依赖:core 模块,其他 feature(可选)

// core 模块
// 职责:提供数据访问、网络、数据库
// 依赖:common 模块

// common 模块
// 职责:提供通用工具、扩展、基类
// 依赖:无(或仅第三方库)

3. Android 模块化实践

3.1 项目结构

MyProject/
├── app/                          # 应用模块
│   ├── build.gradle.kts
│   └── src/main/
├── feature/
│   ├── login/                    # 登录功能
│   │   ├── build.gradle.kts
│   │   └── src/main/
│   ├── home/                     # 首页功能
│   │   ├── build.gradle.kts
│   │   └── src/main/
│   └── profile/                  # 个人中心
│       ├── build.gradle.kts
│       └── src/main/
├── core/
│   ├── network/                  # 网络模块
│   │   ├── build.gradle.kts
│   │   └── src/main/
│   ├── database/                 # 数据库模块
│   │   ├── build.gradle.kts
│   │   └── src/main/
│   └── auth/                     # 认证模块
│       ├── build.gradle.kts
│       └── src/main/
├── common/                       # 通用模块
│   ├── build.gradle.kts
│   └── src/main/
└── build.gradle.kts              # 根构建脚本

3.2 模块配置

kotlin
// app/build.gradle.kts
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "com.example.app"
    
    defaultConfig {
        applicationId = "com.example.app"
    }
}

dependencies {
    // 功能模块
    implementation(project(":feature:login"))
    implementation(project(":feature:home"))
    implementation(project(":feature:profile"))
    
    // 核心模块
    implementation(project(":core:network"))
    implementation(project(":core:database"))
    implementation(project(":core:auth"))
    
    // 通用模块
    implementation(project(":common"))
}

// feature/login/build.gradle.kts
plugins {
    id("com.android.library")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "com.example.feature.login"
}

dependencies {
    // 依赖核心模块
    implementation(project(":core:auth"))
    implementation(project(":core:network"))
    
    // 依赖通用模块
    implementation(project(":common"))
}

// common/build.gradle.kts
plugins {
    id("com.android.library")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "com.example.common"
}

dependencies {
    // 只依赖第三方库,不依赖其他模块
    implementation("androidx.core:core-ktx:1.12.0")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
}

3.3 模块导航

kotlin
// 使用 Navigation 组件
// app/src/main/res/navigation/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">
    
    <include app:graph="@navigation/login_nav_graph" />
    <include app:graph="@navigation/home_nav_graph" />
    <include app:graph="@navigation/profile_nav_graph" />
</navigation>

// feature/login/src/main/res/navigation/login_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/login_nav_graph"
    app:startDestination="@id/loginFragment">
    
    <fragment
        android:id="@+id/loginFragment"
        android:name="com.example.feature.login.LoginFragment" />
</navigation>

4. 模块间通信

4.1 接口暴露

kotlin
// core/auth/src/main/kotlin/AuthProvider.kt
interface AuthProvider {
    fun isLoggedIn(): Boolean
    fun getUserId(): String?
    fun getToken(): String?
}

// core/auth/src/main/kotlin/AuthProviderImpl.kt
class AuthProviderImpl(
    private val preferences: Preferences
) : AuthProvider {
    
    override fun isLoggedIn(): Boolean {
        return preferences.contains("auth_token")
    }
    
    override fun getUserId(): String? {
        return preferences.getString("user_id", null)
    }
    
    override fun getToken(): String? {
        return preferences.getString("auth_token", null)
    }
}

// core/auth/src/main/kotlin/AuthModule.kt
object AuthModule {
    private var instance: AuthProvider? = null
    
    fun init(provider: AuthProvider) {
        instance = provider
    }
    
    fun get(): AuthProvider {
        return instance ?: throw IllegalStateException("AuthProvider not initialized")
    }
}

// app/src/main/kotlin/App.kt
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        
        // 初始化模块
        AuthModule.init(AuthProviderImpl(preferences))
    }
}

// feature/login 中使用
class LoginViewModel : ViewModel() {
    private val auth = AuthModule.get()
    
    fun login(username: String, password: String) {
        // 使用 AuthProvider
    }
}

4.2 路由接口

kotlin
// common/src/main/kotlin/Router.kt
interface Router {
    fun navigateToLogin()
    fun navigateToHome()
    fun navigateToProfile(userId: String)
}

// app/src/main/kotlin/AppRouter.kt
class AppRouter(
    private val navController: NavController
) : Router {
    
    override fun navigateToLogin() {
        navController.navigate(R.id.action_to_login)
    }
    
    override fun navigateToHome() {
        navController.navigate(R.id.action_to_home)
    }
    
    override fun navigateToProfile(userId: String) {
        navController.navigate(R.id.action_to_profile, bundleOf("user_id" to userId))
    }
}

// 模块中使用
class LoginViewModel(
    private val router: Router
) : ViewModel() {
    
    fun onLoginSuccess() {
        router.navigateToHome()
    }
}

4.3 事件总线

kotlin
// common/src/main/kotlin/EventBus.kt
class EventBus private constructor() {
    
    companion object {
        val instance = EventBus()
    }
    
    private val _events = MutableSharedFlow<Event>()
    val events = _events.asSharedFlow()
    
    suspend fun post(event: Event) {
        _events.emit(event)
    }
}

sealed class Event {
    data class LoginSuccess(val userId: String) : Event()
    data class LogoutRequested(val reason: String) : Event()
    data class DataRefreshed(val module: String) : Event()
}

// 发布事件
EventBus.instance.post(Event.LoginSuccess("user123"))

// 订阅事件
viewModelScope.launch {
    EventBus.instance.events.collect { event ->
        when (event) {
            is Event.LoginSuccess -> handleLogin(event.userId)
            is Event.LogoutRequested -> handleLogout(event.reason)
        }
    }
}

4.4 依赖注入 (Hilt)

kotlin
// core/network/src/main/kotlin/NetworkModule.kt
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    
    @Provides
    @Singleton
    fun provideHttpClient(): HttpClient {
        return HttpClient.Builder()
            .install(HttpTimeout)
            .build()
    }
    
    @Provides
    @Singleton
    fun provideApiService(client: HttpClient): ApiService {
        return ApiService(client)
    }
}

// feature/login/src/main/kotlin/LoginModule.kt
@Module
@InstallIn(SingletonComponent::class)
abstract class LoginModule {
    
    @Binds
    @Singleton
    abstract fun bindLoginRepository(
        impl: LoginRepositoryImpl
    ): LoginRepository
}

// app 中 Hilt 自动合并所有模块
@HiltAndroidApp
class App : Application()

5. 依赖管理

5.1 版本目录

toml
// gradle/libs.versions.toml
[versions]
android-gradle = "8.1.0"
kotlin = "1.9.0"
core-ktx = "1.12.0"
lifecycle = "2.6.2"
hilt = "2.48"
retrofit = "2.9.0"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }

[plugins]
android-application = { id = "com.android.application", version.ref = "android-gradle" }
android-library = { id = "com.android.library", version.ref = "android-gradle" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }

5.2 模块依赖规则

kotlin
// 依赖规则:
// app → 可以依赖所有模块
// feature → 可以依赖 core 和 common
// core → 只能依赖 common
// common → 不能依赖其他模块

// 示例:正确的依赖
// app/build.gradle.kts
dependencies {
    implementation(project(":feature:login"))
    implementation(project(":feature:home"))
    implementation(project(":core:network"))
    implementation(project(":common"))
}

// feature/login/build.gradle.kts
dependencies {
    implementation(project(":core:auth"))
    implementation(project(":core:network"))
    implementation(project(":common"))
}

// core/network/build.gradle.kts
dependencies {
    implementation(project(":common"))
}

// common/build.gradle.kts
dependencies {
    // 只依赖第三方库
    implementation(libs.androidx.core.ktx)
}

5.3 避免循环依赖

循环依赖示例(错误):
module A → module B → module A

解决方案:
1. 提取共同依赖到新模块 C
   module A → module C
   module B → module C

2. 使用接口解耦
   module A → interface (在 common 中)
   module B → implements interface

3. 使用依赖注入
   通过 Hilt 管理依赖关系

6. 面试考点

6.1 基础概念

Q1: 什么是模块化?好处是什么?

答案要点:
- 将应用拆分为独立模块
- 好处:
  - 编译速度提升
  - 代码复用
  - 团队独立开发
  - 按需加载
  - 更好的代码组织

Q2: 模块化的分层原则?

答案要点:
- app 层:组装应用
- feature 层:业务功能
- core 层:数据访问
- common 层:通用工具
- 依赖方向:app → feature → core → common

6.2 实战问题

Q3: 模块间如何通信?

kotlin
// 方式 1:接口暴露
interface AuthProvider {
    fun isLoggedIn(): Boolean
}
object AuthModule {
    fun get(): AuthProvider
}

// 方式 2:路由接口
interface Router {
    fun navigateToLogin()
}

// 方式 3:事件总线
EventBus.instance.post(Event.LoginSuccess(userId))

// 方式 4:依赖注入
@HiltAndroidApp
// Hilt 自动合并所有模块

Q4: 如何避免循环依赖?

答案要点:
1. 提取共同依赖到新模块
2. 使用接口解耦
3. 使用依赖注入
4. 设计时明确依赖方向

6.3 高级问题

Q5: 什么是 Dynamic Feature Module?

答案要点:
- Android 的动态功能模块
- 按需下载和安装
- 减少初始 APK 大小
- 使用 Play Core 库
- 配置:dynamicFeature 插件

Q6: 如何优化模块化项目的编译速度?

答案要点:
1. 启用并行编译 (org.gradle.parallel=true)
2. 启用构建缓存
3. 合理拆分模块(避免过小)
4. 使用配置缓存
5. 减少模块间依赖

参考资料


本文完,感谢阅读!