Android 应用开发正在从单纯的功能实现逐渐转向系统架构、运行性能与智能能力协同演进。对于中大型项目而言,模块如何拆分、数据如何流转、页面如何保持流畅,以及 AI 能力如何真正融入业务,已经成为影响研发效率和用户体验的重要因素。
本期 Android Weekly #202507 聚焦三个方向:系统架构设计、性能优化以及 AI 技术融合。通过具体的工程实践,可以更清晰地理解这些技术之间并非相互独立,而是共同构成现代 Android 应用的技术基础。
一、Android 系统架构正在从分层走向可演进
传统 Android 项目经常采用 Activity、Fragment、Service、Repository 等组件进行职责划分。这种方式在项目规模较小时简单直接,但随着业务增长,UI 层容易积累大量状态处理和业务逻辑,Repository 也可能逐渐变成“万能类”,最终导致代码之间形成复杂依赖。
现代 Android 架构更加重视单向数据流、状态管理以及模块边界。
一个较为常见的结构可以抽象为:
UI Layer
↓
ViewModel
↓
Use Case
↓
Repository
↓
Data Source
↓
Network / Database / Local Storage这种结构的价值并不只是让代码看起来更加规整,而是让不同职责具备相对独立的变化空间。
例如,网络接口发生变化时,UI 不应该感知具体的 HTTP 实现;数据库从 Room 切换到其他存储方案时,业务层也不应该大面积修改。通过 Repository 和 Use Case 建立稳定边界,可以降低技术实现变化对业务代码的影响。
1. ViewModel 不应该成为业务逻辑垃圾桶
很多项目引入 ViewModel 后,会把所有业务代码直接塞进其中。短期来看开发速度较快,但随着功能增加,ViewModel 很容易出现数百甚至上千行代码。
更合理的方式是让 ViewModel 负责页面状态和用户交互,而复杂业务通过 Use Case 或领域服务进行组织。
例如:
class UserViewModel(
private val getUserProfile: GetUserProfileUseCase
) : ViewModel() {
private val _uiState = MutableStateFlow(UserUiState.Loading)
val uiState: StateFlow = _uiState
fun loadUser(userId: Long) {
viewModelScope.launch {
_uiState.value = UserUiState.Loading
runCatching {
getUserProfile(userId)
}.onSuccess {
_uiState.value = UserUiState.Success(it)
}.onFailure {
_uiState.value = UserUiState.Error(it)
}
}
}
} 这样做之后,ViewModel 主要负责状态转换,而具体的用户数据获取规则由 Use Case 承担。
2. Compose 进一步强化状态驱动界面
Jetpack Compose 使 Android UI 开发更加接近声明式编程。传统 View 系统通常围绕“找到控件、修改属性、刷新界面”展开,而 Compose 更强调“状态发生变化,UI 根据状态重新计算”。
例如:
@Composable
fun UserPage(state: UserUiState) {
when (state) {
UserUiState.Loading -> LoadingView()
is UserUiState.Success -> UserContent(state.user)
is UserUiState.Error -> ErrorView(state.message)
}
}这样的代码能够直接体现 UI 与状态之间的关系。
不过,Compose 并不意味着可以忽略性能。过度的状态提升、错误的 State 使用方式以及不合理的重组范围,仍然可能导致性能问题。因此架构设计和性能优化需要同时考虑。
二、性能优化需要从“感觉卡”转向数据驱动
Android 性能优化最常见的问题之一,是开发者通过主观感受判断性能。
例如页面打开感觉慢,就不断修改代码;列表滑动感觉卡,就增加缓存;启动时间感觉长,就盲目减少初始化逻辑。
这种方式很难稳定解决问题。
更可靠的流程应该是:
发现问题
↓
采集指标
↓
定位瓶颈
↓
制定优化方案
↓
验证优化结果
↓
持续监控1. 首屏性能需要关注关键路径
应用启动阶段涉及 Application 初始化、依赖注入、数据库创建、SDK 初始化、网络请求以及首页 UI 渲染等多个环节。
如果大量第三方 SDK 都在 Application.onCreate() 中同步初始化,就可能导致冷启动时间明显增加。
例如:
override fun onCreate() {
super.onCreate()
initLogger()
initAnalytics()
initDatabase()
initPush()
initImageLoader()
}这种集中式初始化非常容易形成启动阶段的性能瓶颈。
更合理的策略是区分初始化任务:
首页必须依赖的组件优先初始化;
可以延迟的组件采用懒加载;
非关键任务放到后台执行;
根据业务场景决定是否真正需要初始化;
对初始化耗时进行监控,而不是凭经验判断。
需要特别注意的是,异步化并不等于一定更快。如果首页实际上必须等待某个组件完成初始化,那么把任务放到后台只是隐藏了问题,并没有缩短真实用户路径。
2. UI 卡顿与主线程压力
Android 主线程承担 UI 绘制、事件处理等重要工作。如果主线程执行了大量计算、数据库查询、文件读写或复杂 JSON 解析,就可能出现掉帧。
常见的错误写法包括:
val data = database.userDao().queryAll()直接在主线程执行数据库查询。
更合理的方式是将耗时操作放入适当的后台调度器:
viewModelScope.launch {
val data = withContext(Dispatchers.IO) {
database.userDao().queryAll()
}
updateUi(data)
}但协程调度器也不能被简单理解为“所有代码都丢给 IO”。CPU 密集型任务和磁盘、网络等 I/O 操作需要根据实际特征选择合适的执行环境。
3. 列表性能是 Android 应用的高频问题
长列表页面经常同时涉及布局、图片加载、数据转换和状态更新。
使用 Compose LazyColumn 时,应尽量为列表项提供稳定的 key:
LazyColumn {
items(
items = users,
key = { user -> user.id }
) { user ->
UserItem(user)
}
}稳定的 key 有助于 Compose 正确识别列表项,减少不必要的 UI 工作。
图片加载同样需要考虑缓存、尺寸、解码方式以及生命周期。一个常见问题是为了显示一个 80dp 的头像,却下载并解码数 MB 的原始图片。真正有效的优化应该从数据源、图片尺寸和缓存策略共同入手。
三、内存优化不能只盯着 OOM
Android 中的内存问题并不总是表现为直接崩溃。
图片缓存过大、对象生命周期过长、Context 引用错误、Fragment 或 View 泄漏,都可能导致内存持续增长。
例如,一个单例对象长期持有 Activity:
object Manager {
var activity: Activity? = null
}Activity 即使已经退出,也可能因为引用关系无法及时回收。
对于生命周期敏感的对象,应明确其作用域,避免让长生命周期对象持有短生命周期组件。
排查内存问题时,可以结合 Android Studio Memory Profiler、Heap Dump 等工具观察对象数量和引用链,而不是简单地通过增加内存限制解决问题。
四、模块化让大型 Android 项目更容易维护
当一个 Android 项目不断增加功能时,Gradle Module 很容易从几十个增加到更多。
模块化的真正目标并不是“模块越多越先进”,而是建立清晰的依赖边界。
例如:
:app
:feature:home
:feature:profile
:feature:search
:core:network
:core:database
:core:designsystem
:core:commonFeature 模块负责业务能力,Core 模块负责基础能力。
理想情况下,业务模块不应该随意依赖另一个业务模块内部实现。如果搜索模块需要跳转到用户详情,可以通过导航协议、接口或者公共路由能力完成,而不是直接访问用户模块内部类。
这种边界设计能够降低模块之间的耦合,也有助于提升构建效率和团队并行开发能力。
五、AI 正逐渐成为 Android 应用的新型能力层
AI 与 Android 的结合已经不再局限于“在 App 中增加一个聊天窗口”。
真正具有工程价值的 AI 能力,可以覆盖搜索、推荐、内容生成、智能输入、图片理解、语音交互以及开发辅助等多个场景。
一个典型的 AI 应用架构可以设计为:
Android Client
↓
AI Service Layer
↓
Prompt / Context / Tool
↓
Model
↓
Business Data客户端主要承担交互、上下文展示和结果处理,而模型调用、敏感业务逻辑以及复杂数据访问通常更适合放在服务端。
1. 不要把 API Key 直接放进 Android APK
这是 AI 应用开发中非常重要的一条原则。
如果将模型服务的长期 API Key 直接写入 Android 客户端,即使经过代码混淆,也不能真正防止密钥被提取。
更合理的架构是:
Android App
↓
Business Backend
↓
AI Provider客户端通过自己的业务服务获取受控的 AI 能力,服务端负责鉴权、限流、日志、成本控制以及敏感信息处理。
2. AI 输出需要被当成“不可信输入”
传统业务代码通常认为接口返回的数据遵循既定格式,但生成式 AI 输出具有概率性。
因此,AI 返回结果不能直接作为核心业务逻辑的最终依据。
例如,如果 AI 需要返回结构化数据,可以要求其输出 JSON,并在客户端或服务端进行严格校验:
{
"intent": "search",
"keyword": "Android",
"confidence": 0.92
}实际生产环境还需要考虑字段缺失、格式错误、超长输出和异常内容等情况。
对于涉及业务操作的场景,可以采用:
AI 识别意图
↓
业务系统验证
↓
执行操作
↓
返回结果而不是:
AI 决定结果
↓
直接执行这种边界对于支付、订单、权限和数据修改等场景尤其重要。
六、端侧 AI 与云端 AI 如何选择
Android AI 应用通常存在两种主要方向:云端模型和端侧模型。
云端模型的优势在于模型能力更强、升级更加灵活,适合复杂文本生成、长上下文分析等任务。
端侧模型则更加关注隐私、离线能力和实时响应,适合部分文本分类、智能输入、简单摘要以及设备侧推理等场景。
两者并不是互相替代关系。
可以采用混合架构:
用户请求
↓
判断任务类型
├── 简单任务 → 本地模型
│
└── 复杂任务 → 云端模型这种设计可以根据任务复杂度、网络环境、隐私要求以及设备性能动态选择处理方式。
七、AI 功能也需要性能优化
AI 功能加入 Android 应用之后,性能优化的范围会进一步扩大。
传统应用关注 CPU、内存、GPU、I/O 和网络,而 AI 场景还需要关注模型大小、推理延迟、Token 数量、上下文长度以及网络传输量。
例如,一个简单的问题如果携带大量历史聊天记录,会明显增加请求成本和响应时间。
因此,可以设计上下文管理机制:
完整历史记录
↓
摘要
↓
保留关键上下文
↓
发送模型对于长期对话尤其需要避免无限累积历史消息。
流式输出也是改善 AI 交互体验的重要方式。相比等待完整答案后一次性展示,流式响应可以让用户更早看到生成内容,从而降低主观等待时间。
八、架构、性能和 AI 可以形成统一的工程体系
单独讨论架构、性能和 AI 容易产生割裂感,但在实际项目中,这三个方向往往彼此影响。
例如,一个 AI 搜索页面可能同时包含:
Compose UI;
ViewModel 状态管理;
网络请求;
本地缓存;
AI 推理;
流式响应;
历史记录;
图片或语音输入。
如果没有清晰的架构边界,AI 功能很容易快速侵入 UI 层,最终形成难以维护的代码。
比较合理的组织方式可以是:
UI
↓
ViewModel
↓
AI Search UseCase
↓
Repository
├── Local Cache
├── Business API