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

相关推荐
成都大菠萝10 小时前
Android Car CarProperty 车辆信号链路
android
敲代码的鱼10 小时前
PDF 预览与签名批注写回 支持安卓 iOS 鸿蒙 UTS插件
android·前端·ios
时光足迹12 小时前
uni-app 视频通话实战:康复师与患者视频问诊的 6 个致命 Bug 与解决方案
android·ios·uni-app
Coffeeee16 小时前
闲聊几句,Android老哥们,你们多久没做技改需求了
android·程序员·代码规范
萝卜er17 小时前
Fragment 生命周期与状态恢复-《Android深水区(四)》
android
萝卜er17 小时前
Intent 显式、隐式与 PendingIntent-《Android深水区(五)》
android
Kapaseker19 小时前
一文吃透 Kotlin 集合操作符
android·kotlin
三少爷的鞋20 小时前
Main-safe:现代Android 架构真正的分水岭
android
沐怡旸1 天前
深入解析 Android Performance Analyzer (APA) 底层架构与技术原理
android
李斯维1 天前
从历史的角度看 Android 软件架构
android·架构·android jetpack