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

MVVM架构新闻APP:切换分类后RecyclerView无法从Room更新求解

新闻APP分类切换后RecyclerView内容不更新的问题解决

问题场景

开发新闻APP时,采用Retrofit获取数据、MVVM架构、Room存储本地数据。用户点击分类后,RecyclerView应展示对应分类的新闻列表,但始终显示初始的general分类内容。数据库已完成更新,但UI绑定的articles LiveData始终保留初始数据,问题出在以下代码逻辑:

private var defaultCategory: String = "general"

var articles: LiveData<List<NewsArticleForDatabase>> =
    database.articleDao().getCategoryArticles(defaultCategory)

问题根源

articles变量在Repository初始化时,仅调用一次getCategoryArticles(defaultCategory)获取了对应general分类的LiveData实例。后续修改defaultCategory的值,并不会重新触发DAO的查询——因为LiveData实例已固定,不会自动关联新的分类参数。

解决方案:用switchMap动态响应分类变化

通过在Repository中维护一个可观察的当前分类LiveData,结合Transformations.switchMap,让articles LiveData根据分类变化自动切换查询结果。

1. 修改NewsRepository

private const val TAG = "NewsRepository"

class NewsRepository(private val database: ArticleDatabase) {

    private val COUNTRY: String = "us"
    // 用MutableLiveData存储当前选中的分类,初始值为general
    private val _currentCategory = MutableLiveData<String>().apply { value = "general" }
    
    // 通过switchMap,根据_currentCategory的变化动态获取对应分类的新闻
    val articles: LiveData<List<NewsArticleForDatabase>> = Transformations.switchMap(_currentCategory) { category ->
        database.articleDao().getCategoryArticles(category)
    }

    suspend fun refreshNewsArticles(category: String) {
        _currentCategory.postValue(category) // 更新当前分类,触发switchMap重新查询
        withContext(Dispatchers.IO) {
            val articleList: NewsApiResponse
            try {
                articleList = NewsApi.retrofitService.getHeadlines(api_key, category, COUNTRY)
                Log.d(TAG, "retrofit articles list:- ${articleList.articles.size}")
                // 批量转换为数据库实体类
                val articleForDaoList = articleList.articles.map { article ->
                    getNewsArticleForDatabase(
                        article.source,
                        article.author,
                        article.title,
                        article.description,
                        article.url,
                        article.urlToImage,
                        article.publishedAt,
                        article.content,
                        category
                    )
                }
                // 批量插入,比循环插入更高效
                database.articleDao().upsertAll(articleForDaoList)
                Log.d(TAG,"database size :- ${database.articleDao().getAllArticles().value!!.size} ")
            } catch (e: Exception) {
                Log.d(TAG, e.toString())
            }
        }
        Log.d(TAG,"current category is :- ${_currentCategory.value.toString()}")
    }

    private fun getNewsArticleForDatabase(
        source: Source?,
        author: String?,
        title: String,
        description: String?,
        url: String?,
        urlToImage: String?,
        publishedAt: String?,
        content: String?,
        category: String
    ): NewsArticleForDatabase {
        return NewsArticleForDatabase(
            source,
            author,
            title,
            description,
            url,
            urlToImage,
            publishedAt,
            content,
            category
        )
    }
}

2. 修改ArticleDao,添加批量插入方法

@Dao
interface ArticleDao {

    @Insert (onConflict = OnConflictStrategy.REPLACE)
    suspend fun upsert(article: NewsArticleForDatabase)

    // 新增批量插入方法,提升数据库操作效率
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun upsertAll(articles: List<NewsArticleForDatabase>)

    @Query ("select * from newsArticlesDatabase")
    fun getAllArticles(): LiveData<List<NewsArticleForDatabase>>

    @Delete
    suspend fun delete(article: NewsArticleForDatabase)

    @Query ("select * from newsArticlesDatabase where category = :userCategory")
    fun getCategoryArticles(userCategory: String): LiveData<List<NewsArticleForDatabase>>
}

3. ViewModel与UI绑定无需修改

ViewModel中直接绑定newsRepository.articles即可,因为现在articles会自动根据分类变化更新数据,原有RecyclerView的绑定逻辑保持不变。

关键说明

  • switchMap会监听_currentCategory的变化,每当分类更新时,自动调用DAO的getCategoryArticles获取新的LiveData,并将其作为articles的数据源。
  • 使用postValue更新_currentCategory,确保在IO线程中也能安全更新LiveData。
  • 替换循环插入为批量插入,减少数据库IO操作次数,提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 16:05:20