跳到主要内容

Repository 模式

问题

Repository 模式在 Android 架构中起什么作用?如何实现?

答案

Repository 的定位

Repository 是数据层的统一入口,屏蔽数据来源的细节:

离线优先策略

class ArticleRepository(
private val api: ArticleApi,
private val dao: ArticleDao
) {
// 离线优先:先返回本地 → 后台更新 → 发射最新数据
fun getArticles(): Flow<List<Article>> = flow {
// 1. 先发射本地数据
val local = dao.getAll()
emit(local)

// 2. 请求网络数据
try {
val remote = api.getArticles()
dao.insertAll(remote)
// 3. 发射最新数据
emit(dao.getAll())
} catch (e: IOException) {
// 网络失败,本地数据已经发射过了
if (local.isEmpty()) throw e
}
}

// 使用 Room Flow 自动监听变化(推荐)
fun observeArticles(): Flow<List<Article>> {
// Room Flow 在数据变化时自动发射
refreshArticles()
return dao.observeAll()
}

private fun refreshArticles() {
CoroutineScope(Dispatchers.IO).launch {
try {
val remote = api.getArticles()
dao.insertAll(remote)
// Room Flow 会自动发射新数据
} catch (_: Exception) { }
}
}
}

单一数据源原则(Single Source of Truth)

class UserRepository(
private val api: UserApi,
private val dao: UserDao
) {
// Room 作为唯一数据源
val users: Flow<List<User>> = dao.observeAll()

suspend fun refresh() {
val remoteUsers = api.getUsers()
dao.deleteAllAndInsert(remoteUsers)
// Room Flow 自动通知 UI 更新
}
}
Single Source of Truth

本地数据库(Room)作为唯一数据源,网络数据写入数据库后,UI 通过 Room Flow 获取最新数据。这避免了缓存不一致和数据冲突问题。


常见面试问题

Q1: Repository 应该是单例还是多实例?

答案

通常是单例(通过 Hilt @Singleton 注入),因为 Repository 管理数据源和内存缓存,多实例会导致缓存不一致。单例保证同一数据的请求共享缓存和连接池。

Q2: Repository 如何处理并发请求?

答案

对于相同请求的并发场景(如多个 Fragment 同时请求用户数据),使用 MutexSharedFlow 避免重复请求:

class UserRepository {
private val userCache = MutableStateFlow<User?>(null)
private val mutex = Mutex()

suspend fun getUser(): User {
userCache.value?.let { return it }
return mutex.withLock {
userCache.value ?: api.getUser().also { userCache.value = it }
}
}
}

相关链接