第五章:数据层—网络请求与Repository

第五章:数据层 --- 网络请求与 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 ?: "加载失败")
            }
        }
    }
}

流程:

  1. viewModelScope.launch 开启协程
  2. 调用 ArticleRepository.getArticleList()(suspend)
  3. 成功 → 写入 sourceList + 更新 _uiState
  4. 失败 → 更新 _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 状态

上一章:第四章:Navigation Compose 页面导航 下一章:第六章:UI 组件与 Material3 主题

相关推荐
初雪云5 小时前
让安卓发版再简单一点,体验一键自动化发布
android·运维·自动化
jushi89996 小时前
抖音APP抖音助手增强版 内置逗音小手 支持无水印下载/音频提取/去广告等功能
android·智能手机·音视频
plainGeekDev6 小时前
Android 专家岗 Kotlin 面试题:能答出这些,说明你对语言设计有自己的理解
android·kotlin
plainGeekDev6 小时前
Android 资深岗 Kotlin 面试题:只会用协程不够,你得懂它为什么这么设计
android·kotlin
StarShip7 小时前
第一阶段:应用层视图绘制
android
StarShip7 小时前
第二阶段:RenderThread 渲染处理
android
通玄7 小时前
Jetpack Compose 入门系列(一):从零搭建到基础控件使用
android
StarShip7 小时前
Android 图形渲染流水线完整架构与执行流程分析
android