找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1009

积分

0

好友

131

主题
发表于 昨天 01:51 | 查看: 3| 回复: 0

从 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更新都被堆砌在ActivityFragment中。网络请求常使用AsyncTask,并与界面逻辑深度耦合。

受限于当时设备性能,“功能能运行”是首要目标,代码结构不受重视。这导致单个Activity动辄上千行,成为难以维护的“上帝类”,引发诸多生命周期相关的Bug。代码修改和团队协作变得极其困难,且几乎无法进行有效的单元测试。这一阶段的教训深刻揭示了缺乏关注点分离的代码,最终会被自身的复杂度所吞噬

Phase 2:MVP 时代(2013-2016)

MVP架构示意图

为拯救臃肿的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)

Clean Architecture示意图

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)

MVVM与AAC

谷歌推出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数据流

随着应用功能日益复杂,异步操作与数据流管理成为新的挑战。RxJava的流行将“响应式编程”范式引入Android开发。通过Observable序列封装数据流,并利用丰富的操作符(如mapflatMapswitchMap)处理复杂的异步与转换逻辑,有效解决了回调嵌套问题。

与此同时,单向数据流(Unidirectional Data Flow, UDF)的思想开始萌芽。其核心是数据只能沿单一方向流动(数据源 -> ViewModel -> UI),用户操作则以事件形式通知ViewModel更新数据源,再由新数据驱动UI刷新。这种模式有效避免了双向绑定可能带来的状态混乱。然而,RxJava较高的学习曲线及操作符的滥用也可能导致代码晦涩难懂,这为后续Kotlin协程的崛起埋下了伏笔。

Phase 6:Coroutines + Flow 与 MVVM 普及(2019-2022)

Coroutines与Flow

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)

MVI与Compose

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)接收后处理并更新唯一可信数据源StateCompose函数通过读取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+状态机这样的强规范支撑。贯穿整个演进史的,是开发者对编写更优雅、更健壮代码的不懈追求。




上一篇:Dubbo服务限流从原理到实践:算法、配置与高并发保障
下一篇:Vue3与Three.js打造3D球体抽奖系统:支持年会活动奖品人员可配置
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 15:13 , Processed in 0.108338 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表