第五章:数据层—网络请求与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 主题

相关推荐
plainGeekDev1 小时前
文件读写(Java IO)→ Kotlin 扩展函数
android·java·kotlin
s_nshine2 小时前
释放C盘,迁移studio相关数据到其他盘
android·windows·android studio·内存·c盘
韩曙亮2 小时前
【Flutter】Flutter 中的 Android / iOS 特殊配置 ① ( 网络权限配置 | HTTP 明文传输配置 | 应用名称配置 )
android·网络·flutter·http·ios·网络权限
_李小白2 小时前
【android opencv学习笔记】Day 31:提取轮廓之Canny算法
android·opencv·学习
hashiqimiya3 小时前
每日android布局xml文件
android·xml·gitee
m0_738120723 小时前
渗透测试基础——PHP 序列化数据结构与反序列化机制详解
android·服务器·网络·数据结构·安全·php
故渊at4 小时前
第二板块:Android 四大组件标准化学理 | 第十一篇:组件间通信(IPC)与 Binder 深度解析
android·binder·组件化·组件间通信
ZC跨境爬虫4 小时前
跟着 MDN 学JavaScript day_10:数组——数据的有序集合
android·java·开发语言·前端·javascript
消失的旧时光-19435 小时前
Kotlin 协程设计思想(九):Flow 到底是什么?为什么 suspend 函数还需要 Flow?
android·kotlin·协程·协程异常