Android Weekly #202507:系统架构、性能优化与AI技术融合实践

9 次阅读

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:common

Feature 模块负责业务能力,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