You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于MVVM架构:如何将Retrofit请求的Genre列表存入Room数据库

MVVM+Room+Retrofit 实现网络列表存储与LiveData获取

以下是从网络拉取Genre列表并存入Room,再通过LiveData给UI层提供数据的完整实现方案,核心逻辑集中在Repository层:

1. 定义Room实体类

创建对应数据库表的实体,标记主键和表名:

@Entity(tableName = "genres")
data class Genre(
    @PrimaryKey val id: Int,
    val name: String
)

2. 编写Room Dao接口

Dao层定义数据库操作,包含批量插入(冲突时替换)和获取LiveData列表的方法:

@Dao
interface GenreDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertAll(genres: List<Genre>)

    @Query("SELECT * FROM genres")
    fun getAllGenres(): LiveData<List<Genre>>
}

3. 实现Retrofit网络接口

定义获取Genre列表的API服务,以及Retrofit实例初始化:

interface GenreApiService {
    @GET("genres") // 替换为实际API路径
    suspend fun getGenresFromNetwork(): Response<List<Genre>>
}

// Retrofit实例(建议封装为单例)
val retrofit = Retrofit.Builder()
    .baseUrl("https://your-api-domain.com/") // 替换为实际BaseUrl
    .addConverterFactory(GsonConverterFactory.create())
    .build()

val genreApiService = retrofit.create(GenreApiService::class.java)

4. 核心:Repository层实现

Repository作为数据统一入口,处理网络请求、数据库存储,并对外暴露LiveData:

class GenreRepository(
    private val genreDao: GenreDao,
    private val genreApiService: GenreApiService
) {
    // 直接暴露数据库的LiveData,UI层可观察
    val allGenres: LiveData<List<Genre>> = genreDao.getAllGenres()

    // 刷新逻辑:拉取网络数据后存入数据库
    suspend fun refreshGenres() {
        try {
            val response = genreApiService.getGenresFromNetwork()
            response.body()?.takeIf { response.isSuccessful }?.let {
                genreDao.insertAll(it)
            }
        } catch (e: Exception) {
            // 这里可添加异常处理,比如日志记录、状态通知
            e.printStackTrace()
        }
    }
}

5. ViewModel层对接Repository

ViewModel通过协程调用Repository的刷新方法,同时将LiveData暴露给UI:

class GenreViewModel(
    private val repository: GenreRepository
) : ViewModel() {
    val allGenres = repository.allGenres

    fun refreshGenres() {
        viewModelScope.launch {
            repository.refreshGenres()
        }
    }
}

// ViewModel工厂类(用于注入Repository依赖)
class GenreViewModelFactory(
    private val repository: GenreRepository
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(GenreViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return GenreViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

6. UI层观察数据

在Activity/Fragment中初始化ViewModel,观察LiveData并更新UI:

class GenreActivity : AppCompatActivity() {
    private lateinit var viewModel: GenreViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_genre)

        // 初始化数据库、Repository和ViewModel(实际项目建议用Hilt等DI框架)
        val db = AppDatabase.getInstance(application)
        val repository = GenreRepository(db.genreDao(), genreApiService)
        viewModel = ViewModelProvider(this, GenreViewModelFactory(repository))[GenreViewModel::class.java]

        // 观察数据变化,更新UI
        viewModel.allGenres.observe(this) { genres ->
            updateGenreList(genres)
        }

        // 触发首次数据加载
        viewModel.refreshGenres()
    }

    private fun updateGenreList(genres: List<Genre>) {
        // 实现RecyclerView或其他UI组件的更新逻辑
    }
}

关键注意事项

  • 所有数据库和网络操作必须在协程中执行,Room的suspend方法和Retrofit的suspend方法会自动切换到后台线程。
  • Repository完全隔离数据来源,ViewModel和UI层无需关心数据是来自网络还是本地,符合MVVM的解耦思想。
  • Room返回的LiveData会自动监听数据库变化,数据更新时主动通知UI,无需手动触发刷新。
  • 可扩展:如果需要给UI展示加载/错误状态,可以用密封类封装数据状态,在Repository中返回LiveData<Resource<List<Genre>>>。

内容的提问来源于stack exchange,提问作者zulu_papa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 19:09:32