Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Paging Library LiveData<PagedList<T>> is triggered before the end of the api call

It's been 2 days I'm trying to learn how to use the new Paging Library with the Kotlin Language (first time too)

So I've read many guide / tutorial and Github repo (https://github.com/STAR-ZERO/paging-retrofit-sample) for implementing this paging library and basically my trouble is my LiveData<PagedList<Discover>> inside my ViewModel is trigger before my api call is ending and I don't know why and I feel the call callback.onResult(it?.results.orEmpty(), null, 2) doesn't do anything

I'm using this version android.arch.paging:runtime:1.0.1 You can find the repo of my project here : https://github.com/florian-do/TMDB

logcat :

D/DataSourceFactory: : create()
D/SequentialDataSource: loadInitial:
D/Interceptor: https://api.themoviedb.org/3/discover/movie?api_key=??
D/MainFragment: : observe 0
D/SequentialDataSource: response code -> 200
D/SequentialDataSource: list size: 20

Here there is my code :

Fragment.kt

    val adapter = DiscoverAdapter(context!!, diffCallBack)
    binding.rvFeed.layoutManager = GridLayoutManager(context, 3)
    binding.rvFeed.setHasFixedSize(true)
    binding.rvFeed.adapter = adapter

    viewModel.data.observe(this, Observer {
        Log.d(TAG, ": observe "+it?.size)
    })

MainViewModel.kt

class MainViewModel : ViewModel() {

    var amount = ObservableField<String>()
    val data : LiveData<PagedList<Discover>>

    init {
        val config = PagedList.Config.Builder()
                .setPageSize(20)
                .setEnablePlaceholders(false)
                .build()

        val api : DiscoverService = App.retrofit.create(DiscoverService::class.java)
        val dataSourceFactory = DataSourceFactory(api)

        data = LivePagedListBuilder(dataSourceFactory, config).build()
    }
}

DataSourceFactory.kt

class DataSourceFactory(api: DiscoverService) : DataSource.Factory<Int, Discover>() {
    val source = SequentialDataSource(api)

    override fun create(): DataSource<Int, Discover> {
        return source
    }
}

SequentialDataSource.kt

class SequentialDataSource(val api : DiscoverService) : PageKeyedDataSource<Int, Discover>() {

    private val TAG = "SequentialDataSource"

    override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Discover>) {
        Log.d(TAG, "loadInitial: ")
        api.getDiscover(TMDBClient.API_KEY).enqueue(object : Callback<DiscoverReponse> {
            override fun onFailure(call: Call<DiscoverReponse>, t: Throwable) {
                Log.d(TAG, ": FAIL")
            }

            override fun onResponse(call: Call<DiscoverReponse>, response: Response<DiscoverReponse>) {
                Log.d(TAG, ": response code -> "+response.code())
                val it = response.body();
                Log.d(TAG, "list size: "+it?.results?.size)

                response.body()?.let {
                    callback.onResult(it.results, null, 2)
                }
            }

        })

    }

    override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Discover>) {
        Log.d(TAG, "loadAfter: "+params.key)

    }

    override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Discover>) {
        Log.d(TAG, "loadBefore: "+params.key)
    }
}
like image 306
florian-do Avatar asked Dec 05 '22 10:12

florian-do


2 Answers

Well, after many changed i've found something who fix the problem but it's so weird. If I use enqueue with Retrofit 2.3 it will doesn't work but if i do a .execute() the LiveData is correctly triggered

If someone have a better explanation of this problem your very welcome !

Edit : I've just read the Paging library overview page on android website and i found this :

To display data from a backend server, use the synchronous version of the Retrofit API to load information into your own custom DataSource object.

like image 138
florian-do Avatar answered Dec 29 '22 00:12

florian-do


loadInitial(), loadAfter(), loadBefore() are already asynchronous. If you try to println(Thread.currentThread().name) you will get arch_disk_io_0. To solve this you need to execute code like it's synchronous in order to not miss the callback. You will miss the callback if you switch threads, library is already doing it.

like image 44
Marko Novakovic Avatar answered Dec 29 '22 00:12

Marko Novakovic