跳到主要内容

设计持久化层

问题

如何设计 Android 客户端的数据持久化层?

答案

分层架构

离线优先策略(Single Source of Truth)

// Repository 实现离线优先
class ArticleRepository(
private val api: ArticleApi,
private val dao: ArticleDao
) {
// Room 数据库作为 Single Source of Truth
fun getArticles(): Flow<List<Article>> = dao.getAllArticles()

// 从网络刷新数据,写入数据库
suspend fun refresh(): Result<Unit> = try {
val articles = api.getArticles()
dao.upsertAll(articles) // 写入数据库
Result.Success(Unit)
} catch (e: Exception) {
Result.Error(e)
}
}
// ViewModel
class ArticleViewModel(
private val repository: ArticleRepository
) : ViewModel() {
// UI 始终观察数据库数据
val articles = repository.getArticles()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())

fun refresh() {
viewModelScope.launch {
_isRefreshing.value = true
repository.refresh()
_isRefreshing.value = false
}
}
}

存储方案选型

数据类型推荐方案原因
用户设置DataStore类型安全、协程支持
结构化数据RoomSQL 查询、关系映射
缓存K-VMMKV高性能、跨进程
文件、图片File + 内部存储权限隔离
临时缓存LruCache + DiskLruCache自动淘汰

数据库版本迁移

val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE articles ADD COLUMN author TEXT NOT NULL DEFAULT ''")
}
}

val db = Room.databaseBuilder(context, AppDatabase::class.java, "app.db")
.addMigrations(MIGRATION_1_2)
.build()

常见面试问题

Q1: 什么是 Single Source of Truth?为什么推荐这种模式?

答案

Single Source of Truth(SSOT)模式中,UI 只观察本地数据库(Room),网络数据先写入数据库再由数据库通知 UI 更新。好处是:

  1. 离线可用:无网络时展示本地数据
  2. 一致性:所有数据来源统一,避免 UI 显示的是缓存数据而非最新数据
  3. 简化逻辑:UI 层只需观察一个数据源(Flow/LiveData),无需处理网络和数据库的合并

相关链接