第五章:数据层 --- 网络请求与 Repository
数据层的职责:统一管理数据来源(网络/本地),为 ViewModel 提供干净的数据接口。
5.1 Retrofit 网络请求
ApiService 接口定义:
kotlin
interface ApiService {
@GET("products")
suspend fun getArticleList(@Query("limit") limit: Int): ProductListResponse
}
RetrofitManager 单例:
kotlin
object RetrofitManager {
private const val BASE_URL = "https://dummyjson.com/"
val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
为什么用 suspend?
- 挂起函数,在协程中执行,不阻塞主线程
- 网络请求是耗时操作,必须在后台执行
5.2 数据模型
kotlin
// ArticleBean.kt - 商品/文章数据模型
data class ArticleBean(
val id: Int,
val title: String,
val description: String,
val thumbnail: String,
val price: Double = 0.0,
val rating: Double = 0.0,
val brand: String = "",
val category: String = "",
val stock: Int = 0,
)
// ProductListResponse.kt - API 响应包装
data class ProductListResponse(
val products: List<ArticleBean>,
val total: Int = 0,
val skip: Int = 0,
val limit: Int = 0,
)
JSON 响应示例:
json
{
"products": [...],
"total": 100,
"skip": 0,
"limit": 10
}
GsonConverterFactory 自动将 JSON 解析为 data class。
5.3 Repository 模式
ArticleRepository --- 文章列表仓库:
kotlin
object ArticleRepository {
private var cachedArticles: List<ArticleBean> = emptyList()
suspend fun getArticleList(): List<ArticleBean> {
val list = RetrofitManager.apiService.getArticleList(10).products
cachedArticles = list // 写入内存缓存
return list
}
fun getArticleById(id: Int): ArticleBean? =
cachedArticles.find { it.id == id } // 从缓存查询
}
ProfileRepository --- 用户仓库(本地示例数据):
kotlin
object ProfileRepository {
suspend fun getProfile(): ProfileUser {
delay(300) // 模拟网络延迟
return demoProfileUser // 返回本地示例数据
}
}
5.4 内存缓存策略
kotlin
// 首页请求数据 → 写入 cachedArticles
// 详情页按 ID 查询 → 从 cachedArticles 查找
object ArticleRepository {
private var cachedArticles: List<ArticleBean> = emptyList()
suspend fun getArticleList(): List<ArticleBean> {
val list = RetrofitManager.apiService.getArticleList(10).products
cachedArticles = list // 缓存
return list
}
fun getArticleById(id: Int): ArticleBean? =
cachedArticles.find { it.id == id }
}
缓存特点:
| 特性 | 说明 |
|---|---|
| 内存缓存 | 保存在对象变量中,应用关闭消失 |
| 单例模式 | object 单例,全局唯一 |
| 查找方式 | find by id |
| 局限性 | 详情页依赖列表先执行 |
5.5 ViewModel 中调用 Repository
kotlin
class HomeViewModel : ViewModel() {
private var sourceList = mutableListOf<ArticleBean>()
private val _uiState = MutableStateFlow<HomeUiState>(HomeUiState.Loading)
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
init { loadData() }
fun loadData() {
viewModelScope.launch {
_uiState.value = HomeUiState.Loading
try {
val result = ArticleRepository.getArticleList() // suspend 调用
sourceList = result.toMutableList()
_uiState.value = HomeUiState.Success(articles = result)
} catch (e: Exception) {
_uiState.value = HomeUiState.Error(e.message ?: "加载失败")
}
}
}
}
流程:
viewModelScope.launch开启协程- 调用
ArticleRepository.getArticleList()(suspend) - 成功 → 写入 sourceList + 更新 _uiState
- 失败 → 更新 _uiState 为 Error
5.6 错误处理
网络请求可能失败,ViewModel 需要捕获异常:
kotlin
viewModelScope.launch {
_uiState.value = HomeUiState.Loading
try {
val result = ArticleRepository.getArticleList()
_uiState.value = HomeUiState.Success(articles = result)
} catch (e: Exception) {
e.printStackTrace()
_uiState.value = HomeUiState.Error(
message = e.message ?: "加载失败,请稍后重试"
)
}
}
Composable 根据 UiState 显示不同界面:
kotlin
when (uiState) {
is HomeUiState.Loading -> CircularProgressIndicator()
is HomeUiState.Error -> ErrorView(message = uiState.message, onRetry = onRetry)
is HomeUiState.Success -> SuccessView(articles = uiState.articles)
}
5.7 总结
- Retrofit + Gson 管理网络请求
- ApiService 定义接口,RetrofitManager 构建实例
- Repository 模式统一数据来源(网络/本地)
- ArticleRepository 同时负责网络获取与内存缓存
- ViewModel 通过 suspend 函数调用 Repository
- try-catch 捕获异常,更新 Error 状态