从 2010 年至今,Android 架构的演进始终围绕着“解耦、可维护、可测试”的核心目标,推动开发者从最初的“代码堆砌”逐步迈向“规范化设计”。本文将系统梳理这15年间的7个关键阶段,揭示其背后的技术脉络与核心逻辑。
TLDR:各阶段概览
| 时间范围 |
核心技术/架构 |
核心特点 |
适用场景 |
| 2010-2013 |
无架构,裸写 Activity/Fragment |
开发快、新手易上手;代码臃肿耦合、不可测试、维护难 |
超小型 Demo、一次性功能验证 |
| 2013-2016 |
MVP + 手动依赖管理 |
关注点分离、Activity 瘦身、Presenter 可测;易内存泄漏、回调嵌套 |
中小型 App、基础分层需求 |
| 2014-2017 |
Clean Architecture + Dagger |
业务与框架解耦、复用性强;分层复杂、开发成本高 |
中大型 App、长期可维护性项目 |
| 2016-2019 |
MVVM + AAC + Repository |
生命周期感知、响应式绑定、数据统一;DataBinding 可读性争议 |
绝大多数中小型 App |
| 2017-2020 |
MVVM + RxJava + 单向数据流萌芽 |
异步逻辑简洁、数据流可控;学习成本高、代码易晦涩 |
中大型 App、复杂异步场景 |
| 2019-2022 |
MVVM + Coroutines + Flow |
异步代码简洁、背压友好、状态可控;需掌握 Kotlin 协程 |
主流中小到中大型 App |
| 2020-2025 |
MVI + Jetpack Compose + 状态机 |
状态可预测、声明式 UI 高效、测试友好;学习门槛高 |
大型复杂 App、极致可维护性需求 |
Phase 1:前架构时代(2010-2013)
这是 Android 开发的早期阶段,几乎没有架构概念。所有业务逻辑、网络请求、数据解析和UI更新都被堆砌在Activity或Fragment中。网络请求常使用AsyncTask,并与界面逻辑深度耦合。
受限于当时设备性能,“功能能运行”是首要目标,代码结构不受重视。这导致单个Activity动辄上千行,成为难以维护的“上帝类”,引发诸多生命周期相关的Bug。代码修改和团队协作变得极其困难,且几乎无法进行有效的单元测试。这一阶段的教训深刻揭示了缺乏关注点分离的代码,最终会被自身的复杂度所吞噬。
Phase 2:MVP 时代(2013-2016)

为拯救臃肿的Activity,MVP(Model-View-Presenter)模式开始普及。它将职责明确划分:View(由Activity/Fragment充当)负责UI渲染和事件转发;Presenter作为中间层处理业务逻辑;Model则封装数据操作。
这一分层显著精简了Activity,并使Presenter具备了独立进行单元测试的能力。但其缺点同样明显:复杂页面下Presenter可能变得臃肿,回调嵌套形成“回调地狱”,且因Presenter持有View引用,不当的生命周期管理易引发内存泄漏。尽管如此,MVP首次大规模实践了“关注点分离”,为后续架构演进奠定了基础。
interface LoginView {
fun showLoading()
fun showError(msg: String)
fun showSuccess(user: User)
}
class LoginPresenter(
private val authRepo: AuthRepository,
private val view: LoginView
) {
fun onLoginClick(email: String, pass: String) {
view.showLoading()
authRepo.login(email, pass,
onSuccess = { view.showSuccess(it) },
onError = { view.showError(it.message ?: "Unknown error") }
)
}
}
Phase 3:Clean Architecture 与依赖注入(2014-2017)

MVP解决了页面层的臃肿,但业务逻辑与框架的耦合依然存在。Clean Architecture(整洁架构)与依赖注入框架(如Dagger)的到来,旨在解决这一问题。Clean Architecture通过分层(如实体层、用例层、接口适配器层、框架层)确保核心业务逻辑独立于任何外部框架,依赖关系由外向内。
同时,Dagger等依赖注入框架的普及,替代了手动创建和管理对象依赖的繁琐方式,使得各层之间的依赖关系更加清晰、可测试。这一阶段极大地提升了代码的可复用性和可测试性,但也因分层过多而提高了小型项目的开发成本。
Clean Architecture首次明确引入了领域层(Domain Layer),并通过用例(UseCase)封装特定业务规则。
// Domain layer (纯 Kotlin,不依赖Android框架)
data class User(val id: String, val email: String)
interface AuthRepository {
fun login(email: String, password: String): Single<User>
}
class LoginUseCase(private val repo: AuthRepository) {
fun execute(email: String, pass: String) = repo.login(email, pass)
}
同时,“仓库模式(Repository)”开始成为标准实践,统一封装网络与本地数据源访问,对上层提供一致的数据接口。
// Data Layer - 数据源实现细节在此
class AuthRepositoryImpl(private val api: AuthApi) : AuthRepository {
override fun login(email: String, pass: String) =
api.login(LoginRequest(email, pass)).map { dto -> dto.toDomain() }
}
interface AuthApi {
@POST("auth/login")
fun login(@Body body: LoginRequest): Single<UserDto>
}
依赖注入(DI)框架负责自动连接各层,组装所需组件。
// DI: Dagger 2 自动连接各层
@Module
abstract class RepositoryModule {
@Binds
abstract fun bindAuthRepository(impl: AuthRepositoryImpl): AuthRepository
}
Phase 4:MVVM 与架构组件(2016-2019)

谷歌推出Android Architecture Components(AAC,包含ViewModel、LiveData、Room等),直接推动了MVVM(Model-View-ViewModel)模式成为主流。
ViewModel负责管理与UI相关的数据,其生命周期独立于Activity,有效避免了配置变更(如屏幕旋转)导致的数据丢失。LiveData作为一种生命周期感知的响应式数据容器,能在数据变化时自动通知处于活跃状态的观察者。Room则极大简化了SQLite数据库操作。
class LoginViewModel(
private val authRepo: AuthRepository
) : ViewModel() {
private val _state = MutableLiveData<LoginState>(LoginState.Idle)
val state: LiveData<LoginState> = _state
fun login(email: String, pass: String) = viewModelScope.launch {
_state.value = LoginState.Loading
runCatching { authRepo.login(email, pass) }
.onSuccess { _state.value = LoginState.Success(it) }
.onFailure { _state.value = LoginState.Error(it.message ?: "Oops") }
}
}
sealed class LoginState {
data object Idle : LoginState()
data object Loading : LoginState()
data class Success(val user: User) : LoginState()
data class Error(val msg: String) : LoginState()
}
MVVM使代码结构更清晰:Activity/Fragment专注于观察数据并更新UI,ViewModel专注于处理业务逻辑。数据和UI的绑定变得更加自然。不过,早期与MVVM绑定的DataBinding在XML中编写过多逻辑也引发过可读性争议。
// 在Activity/Fragment中观察状态
viewModel.state.observe(this) { state ->
when (state) {
is LoginState.Loading -> showProgress()
is LoginState.Success -> showWelcome(state.user)
is LoginState.Error -> showError(state.msg)
else -> Unit
}
}
Phase 5:RxJava 与单向数据流萌芽(2017-2020)

随着应用功能日益复杂,异步操作与数据流管理成为新的挑战。RxJava的流行将“响应式编程”范式引入Android开发。通过Observable序列封装数据流,并利用丰富的操作符(如map、flatMap、switchMap)处理复杂的异步与转换逻辑,有效解决了回调嵌套问题。
与此同时,单向数据流(Unidirectional Data Flow, UDF)的思想开始萌芽。其核心是数据只能沿单一方向流动(数据源 -> ViewModel -> UI),用户操作则以事件形式通知ViewModel更新数据源,再由新数据驱动UI刷新。这种模式有效避免了双向绑定可能带来的状态混乱。然而,RxJava较高的学习曲线及操作符的滥用也可能导致代码晦涩难懂,这为后续Kotlin协程的崛起埋下了伏笔。
Phase 6:Coroutines + Flow 与 MVVM 普及(2019-2022)

Kotlin协程(Coroutines)和Flow的组合,以其更简洁、直观的语法,解决了RxJava的复杂性痛点。协程提供了更轻量级的异步处理能力,而Flow则实现了响应式数据流。
在此阶段,MVVM模式彻底巩固了其默认架构的地位。ViewModel利用协程处理异步任务,使用Flow封装数据流。LiveData因其在Kotlin协程环境中的局限性,逐渐被功能更强大、支持背压的StateFlow/SharedFlow所替代。
class LoginViewModel(
private val authRepo: AuthRepository
) : ViewModel() {
var uiState by mutableStateOf<LoginUiState>(LoginUiState.Idle)
private set
fun login(email: String, pass: String) = viewModelScope.launch {
uiState = LoginUiState.Loading
try {
val user = authRepo.login(email, pass) // 挂起函数
uiState = LoginUiState.Success(user)
} catch (e: Exception) {
uiState = LoginUiState.Error(e.message ?: "Network error")
}
}
}
sealed class LoginUiState {
data object Idle : LoginUiState()
data object Loading : LoginUiState()
data class Success(val user: User) : LoginUiState()
data class Error(val msg: String) : LoginUiState()
}
“单向数据流”的实践也趋于成熟,页面状态(如空闲、加载中、成功、失败)通常被封装为密封类,状态迁移更加可控。MVVM + Coroutines + Flow 的组合,在简洁性、可维护性和功能性之间取得了良好平衡,成为大多数项目的首选架构。
// 使用Flow实现定时刷新的数据源
class UserRepository(
private val api: UserApi
) {
fun observeUser(id: String): Flow<User> = flow {
while (true) {
emit(api.getUser(id)) // 发射最新用户数据
delay(10_000) // 每10秒刷新一次
}
}.flowOn(Dispatchers.IO)
}
// 在ViewModel中收集Flow
viewModelScope.launch {
userRepo.observeUser("42").collect { user ->
uiState = LoginUiState.Success(user)
}
}
Phase 7:Compose 与 MVI 成熟(2020-2025)

Jetpack Compose的发布标志着Android进入“声明式UI”时代,与之伴生的架构模式也朝着MVI(Model-View-Intent)与状态机方向演进。
@Composable
fun LoginScreen(viewModel: LoginViewModel = hiltViewModel()) {
val state by viewModel.state.collectAsState()
Column(modifier = Modifier.padding(16.dp)) {
OutlinedTextField(
value = state.email,
onValueChange = viewModel::onEmailChange,
label = { Text("Email") }
)
Button(
onClick = viewModel::onLoginClick,
enabled = !state.loading
) {
if (state.loading) CircularProgressIndicator()
else Text("Login")
}
state.error?.let { Text(it, color = Color.Red) }
state.user?.let { Text("Welcome ${it.email}") }
}
}
Compose允许开发者使用Kotlin代码声明UI,通过“状态驱动界面重组”替代了传统的XML命令式操作。MVI则是单向数据流的极致体现:View发送Intent(即用户动作),ViewModel(或称为Store/StateHolder)接收后处理并更新唯一可信数据源State,Compose函数通过读取State自动重组UI。
class LoginViewModel(
private val authRepo: AuthRepository
) : ViewModel() {
private val _state = MutableStateFlow(LoginUiState())
val state = _state.asStateFlow()
fun onEmailChange(value: String) {
_state.update { it.copy(email = value) }
}
fun onLoginClick() = viewModelScope.launch {
_state.update { it.copy(loading = true, error = null) }
runCatching { authRepo.login(_state.value.email, _state.value.password) }
.onSuccess { user -> _state.update { it.copy(loading = false, user = user) } }
.onFailure { e -> _state.update { it.copy(loading = false, error = e.message) } }
}
}
data class LoginUiState(
val email: String = "",
val password: String = "",
val loading: Boolean = false,
val user: User? = null,
val error: String? = null
)
此外,“状态机”模式成为管理复杂页面逻辑的标配。通过明确定义页面的所有可能状态(枚举)及状态转移条件,将状态变化逻辑显式地集中在ViewModel中,使得状态流转完全可预测、可测试。目前,MVI + 状态机 + Compose 的组合被视为处理大型复杂应用的理想架构,在保证代码结构清晰的同时,显著提升了复杂逻辑的可维护性。
演进的核心逻辑
纵观这七个阶段,Android架构的演进路径清晰可见:从“功能优先”转向“架构优先”,从“高度耦合”走向“分层解耦”,从“命令式编程”演化为“响应式+声明式编程”。每个新阶段的出现都是为了解决前一阶段暴露出的核心痛点。实践表明,不存在“放之四海而皆准”的最佳架构,只有“最适合当前项目规模与团队现状”的架构。小型演示项目无需过度设计,而大型长期维护的项目则需要MVI+状态机这样的强规范支撑。贯穿整个演进史的,是开发者对编写更优雅、更健壮代码的不懈追求。