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
相关产品推荐
相关产品推荐

