Search code examples
androidkotlinretrofit2android-mvvmandroid-diffutils

Retrofit responses keep the old data and add the new one to that for editText search


I am getting data from an API with editText search. At first search it works as expected but on second and so on, it will not show the only new response, instead it keeps old one and adds new one to end of it. It acts like it's caching previous ones. How can i fix that to show only last search word results?

Fragment:

var job: Job? = null
binding.etSearchNews.addTextChangedListener { editable ->
    job?.cancel()
    job = MainScope().launch {
        delay(Constants.SEARCH_DELAY)
        editable?.let {
            if (editable.toString().isNotEmpty()) {
                viewModel.searchNews(editable.toString())
            }
        }
    }
}

viewModel.searchNews.observe(viewLifecycleOwner) {
    when (it) {
        is Resource.Success -> {
            hideProgressBar()
            it.data?.let { response ->
                newsAdapter.differ.submitList(response.articles.toList())
            }
        }
        is Resource.Error -> {}
        is Resource.Loading -> {}
    }
}

Adapter:

private val differCallback = object: DiffUtil.ItemCallback<Article>() {
    override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean {
        return oldItem.url == newItem.url
    }

    override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean {
        return oldItem == newItem
    }
}

val differ = AsyncListDiffer(this, differCallback)

API:

@GET("v2/everything")
suspend fun searchNews(
    @Query("q") query: String,
    @Query("page") number: Int = 1,
    @Query("pageSize") size: Int = Constants.PAGE_SIZE,
    @Query("apiKey") key: String = Constants.API_KEY
): Response<NewsResponse>

I've tried to add but no luck:

@Headers("Cache-Control: no-cache") 

Solution

  • After spending a day to solve this, I've found the answer finally. It was because of i haven't properly clear the response before. It needs to be cleaned in addTextChangedListener so every new try will start on fresh response.

    Also for leaving the fragment and coming back to it data refresh and scroll position changes problem, i added hasFocus to avoid it:

    So, SearchFragment looks like this:

            var job: Job? = null
            binding.etSearchNews.addTextChangedListener { editable ->
                job?.cancel()
                job = MainScope().launch {
                    delay(Constants.SEARCH_DELAY)
                    editable?.let {
                        if (editable.toString().isNotEmpty()) {
                            if (binding.etSearchNews.hasFocus()) {
                                viewModel.searchNewsResponse = null
                                viewModel.searchNewsPage = 1
                                viewModel.searchNews(editable.toString())
                            }
                        } else {
                            newsAdapter.differ.submitList(listOf())
                        }
                    }
                }
            }
    

    P.S. I didn't see that code in the tutorial, watched twice that part. I hope it helps to others.