Search code examples
android-jetpack-composeretrofit

Different query results when re-executing with Retrofit in Android Studio


I am writing a book search application. In it I created a Retrofit service to fetch data from Google Books API. When I query with q=domani multiple times the query result is sometimes different each time.

enter image description here

Below is the code that executes the query in the viewmodel:

fun getPhotos(query: String) {
    viewModelScope.launch {
        fetchUiState = FetchUiState.Loading
        fetchUiState = try {
            var thumbnails = bookRepository.getBooks(query)
                .items.map { it.volumeInfo.imageLinks?.thumbnail }
            thumbnails=thumbnails.filterNotNull()
            Log.d("getPhotos", "${thumbnails.size} Thumbnails: $thumbnails")

            FetchUiState.Success(thumbnails)
        } catch (e: HttpException) {
            Log.e("getPhotos", "HTTP exception occurred", e)
            FetchUiState.Error
        } catch (e: IOException) {
            Log.e("getPhotos", "Network or IO exception occurred", e)
            FetchUiState.Error
        } catch (e: Exception) {
            Log.e("getPhotos", "Unexpected exception occurred", e)
            FetchUiState.Error
        }
    }
}

Log: enter image description here

Declarations for Retrofit:

interface BookApiService{
@GET("volumes")suspend fun getBooks(@Query("q") query: String): QueryResult
}

interface AppContainer {
val bookRepository: BookRepository
}
class DefaultAppContainer : AppContainer {

private val json = Json {
    ignoreUnknownKeys = true
}
private val baseUrl="https://www.googleapis.com/books/v1/"
private val retrofit: Retrofit = Retrofit.Builder()
    .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
    .baseUrl(baseUrl)
    .build()

private  val retrofitService: BookApiService by lazy {
    retrofit.create(BookApiService::class.java)
}
override val bookRepository: BookRepository by lazy {
    NetWorkBookRepository(retrofitService)
}

}

Data layer:

@Serializable
data class QueryResult(
val items: List<BookInfo>
)

@Serializable
data class BookInfo(
val id: String,
val volumeInfo: VolumeInfo
)

@Serializable
data class VolumeInfo(
val imageLinks: ImageLinks? = null,
)

@Serializable
data class ImageLinks(
val thumbnail: String
)

Solution

  • The reason for the behavior you are observing is pagination. The Books API does not return all search results at once, but instead it by default returns batches of 10. You can test it by calling the URL from the browser:

    https://www.googleapis.com/books/v1/volumes?q=domani

    You can see that the totalItems are 812, and at each refresh, you might be getting a different subset of these. By default, the results are sorted by relevance, but if two books have the same relevance, it appears that there is no order guaranteed.

    Please have a look at the official documentation:

    Pagination You can paginate the volumes list by specifying two values in the parameters for the request:

    startIndex - The position in the collection at which to start. The index of the first item is 0.
    maxResults - The maximum number of results to return. The default is 10, and the maximum allowable value is 40.

    So each time that you execute the request, the API may return a different batch of 10 search results. And out of these 10 search results, there might be a varying number of results that has a thumbnail assigned, which you are filtering for.