Appearance
模块化架构
字数统计:约 10000 字
难度等级:⭐⭐⭐⭐
面试重要度:⭐⭐⭐⭐⭐
目录
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 → common2.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 → common6.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. 减少模块间依赖参考资料
本文完,感谢阅读!