paging 3 медленно загружает данные не работает повторная загрузка данных при ошибке

Реализовал pagging3 c их отображением в recyclerview. Но столкнулся с тем, что идет очень большая задержка загрузки данных, при чем количество элементов загружаемых с Api на картину практически не влияет. Отсюда пришел к выводу, что проблема в коде. pagging 3 первый раз реализовывал, по этому по любому куча ошибок. Вопрос, что мне стоит изменить, что бы заставить его работать быстрее. еще api, которое я использую немного специфично и возвращает мне список элементов с их именами и ссылками на них. И что бы получить информацию о каком либо из этих объектов мне нужно снова обратиться к api по ссылке или сделать запрос по имени объекта. И после уже отображать объект в моём recyclerview.

{"count":1118,"next":"https://pokeapi.co/api/v2/pokemon/ offset=5&limit=5","previous":null,"results":[{"name":"bulbasaur","url":"https://pokeapi.co/api/v2/pokemon/1/"},{"name":"ivysaur","url":"https://pokeapi.co/api/v2/pokemon/2/"},{"name":"venusaur","url":"https://pokeapi.co/api/v2/pokemon/3/"},{"name":"charmander","url":"https://pokeapi.co/api/v2/pokemon/4/"},{"name":"charmeleon","url":"https://pokeapi.co/api/v2/pokemon/5/"}]}

Вот код моих классов:

Интерфейс описывающий взаимодействие с API, а так же объект возвращающий экземпляр retrofit.

interface PokemonApiService {

    @GET("pokemon")
    suspend fun getResponseApi(
        @Query("offset") offset: Int,
        @Query("limit") limit: Int
    ): ResponseApi

    @GET("pokemon/{name}")
    suspend fun getPokemonByName(@Path("name") name: String): Pokemon
}

object PokemonApi {
    val retrofitService: PokemonApiService by lazy {
        retrofit.create(PokemonApiService::class.java)
    }
}

PagingSource получающий данные для моих page от api и возвращающий их в LoadResult

class PokemonPagingSource(private val service: PokemonApiService) : PagingSource<Int, Pokemon>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Pokemon> {
            var keyNumber = params.key?: START_INDEX
        return try {
            val responseApi = service.getResponseApi(keyNumber, params.loadSize)

            val count = responseApi.count!!

            val results = responseApi.pokemonResults as List<PokemonResult>
            val pokemon: MutableList<Pokemon> = mutableListOf()

                for (result in results) {
                    pokemon.add(
                        service.getPokemonByName(result.name ?: "")
                    )
            }
            var nextKey = if(keyNumber == START_INDEX) PAGE_SIZE * 3 else keyNumber + PAGE_SIZE
            var prevKey = if (keyNumber >= PAGE_SIZE) keyNumber - PAGE_SIZE else null

            if ((keyNumber + PAGE_SIZE) >= count) {
                nextKey = START_INDEX
                prevKey = null
            }
            LoadResult.Page(data = pokemon, prevKey = prevKey, nextKey = nextKey)
        } catch (e: IOException) {
            Timber.v(e.message.toString())
            LoadResult.Error(e)
        } catch (e: HttpException) {
            Timber.v(e.message.toString())
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, Pokemon>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(PAGE_SIZE)
                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(PAGE_SIZE)
        }
    }

    companion object {
        const val PAGE_SIZE = 30
        const val START_INDEX = 0
    }
}

Репозиторий который создает мой Flow c данным полученными из pagingsource

class PokemonRepository {
    private val service = PokemonApi.retrofitService
    fun getResults(): Flow<PagingData<Pokemon>> {
        return Pager(
            config = PagingConfig(
                pageSize = PAGE_SIZE,
                enablePlaceholders = false
            ),
            pagingSourceFactory = { PokemonPagingSource(service) }
        ).flow
    }
}

Класс адаптер для recycleview, так же в этом классе, я через glide получаю изображение объекта по url

class PokemonAdapter() : PagingDataAdapter<Pokemon, PokemonAdapter.PokemonViewHolder>(PokemonComparator) {

    class PokemonViewHolder(private val binding: PokemonItemBinding): RecyclerView.ViewHolder(binding.root) {
        fun bind(pokemon: Pokemon?) {
            binding.nameTxt.text = pokemon?.name
            pokemon?.sprites?.frontDefault?.let{
                val imgUrl = it.toUri().buildUpon().scheme("https").build()

                Glide.with(binding.imagePokemonList.context)
                    .load(imgUrl)
                    .into(binding.imagePokemonList)
            }
            binding.textView2.text = pokemon?.id.toString()
        }

    }

    override fun onBindViewHolder(holder: PokemonViewHolder, position: Int) {
        val result = getItem(position)
        holder.bind(result)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PokemonViewHolder {
        return PokemonViewHolder(
            PokemonItemBinding.inflate(
                LayoutInflater.from(parent.context), parent, false
            )
        )
    }
}

object PokemonComparator : DiffUtil.ItemCallback<Pokemon>() {
    override fun areItemsTheSame(oldItem: Pokemon, newItem: Pokemon): Boolean {
        // Id is unique.
        return oldItem.id == newItem.id
    }

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

Адаптер для для обработки состояний загрузки pagging 3.

class PokemonLoadStateAdapter(
    private val retry: () -> Unit
) : LoadStateAdapter<PokemonLoadStateAdapter.LoadStateViewHolder>() {

    override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
        holder.bind(loadState)
    }

    override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder {
        return LoadStateViewHolder(
            LoadStatePokemonBinding.inflate(
                LayoutInflater.from(parent.context), parent, false
            ), retry
        )
    }

    class LoadStateViewHolder(private val binding: LoadStatePokemonBinding, private val retry: () -> Unit)
        : RecyclerView.ViewHolder(binding.root) {
        fun bind(loadState: LoadState) {
            binding.buttonRetry.isVisible = loadState !is LoadState.Loading
            binding.errorTxt.isVisible = loadState !is LoadState.Loading
            binding.progressBar.isVisible = loadState is LoadState.Loading

            if (loadState is LoadState.Error) {
                binding.errorTxt.text = loadState.error.localizedMessage
            }

            binding.buttonRetry.setOnClickListener  {
                retry()
            }
        }
    }
}

Класс ViewModel, которая хранит в себе Flow полученный от репозитория

class PokemonListViewModel : ViewModel() {
val pokemon = PokemonRepository().getResults().cachedIn(viewModelScope)
}

onViewCreated метод фрагмента в котором создаю адаптер для recyclerview, адаптер отвечающий за loadstate у paging3 и передаю данные в recyclerview через его адаптер.

verride fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val pokemonAdapter = PokemonAdapter()
        val pokemonList = binding.pokemonList
        pokemonList.apply {
            setHasFixedSize(true)
            adapter = pokemonAdapter.withLoadStateFooter(
                footer = PokemonLoadStateAdapter {
                    pokemonAdapter.retry()
                }
            )
        }
        viewLifecycleOwner.lifecycleScope.launch {
            viewModel.pokemon.collectLatest { pokemonAdapter.submitData(it) }
        }
    }

Сначала приложение долго загружает данные и я получаю белый экран введите сюда описание изображения

Затем начинается отображение элементов списка, но это происходит медленно и кусками которые меньше размера моей страницы, progressbar при этом не появляется. введите сюда описание изображения

Но если проскролить экран в самый низ, то будет белый экран и посреди него маленький progresbar, хотя по идее он должен быть в низу recyclerview и отображаться на всю его ширину во время загрузки любого элемента, как я понимаю. введите сюда описание изображения

А в случае обрыва связи(Отключаю интернет на смартфоне), он отображает сообщение с ошибкой и кнопкой в рамках одного item списка. При чем нажатие на кнопку повторную загрузку не осуществляет, при условии, что связь уже полностью восстановлена.

введите сюда описание изображения

Что стоит изменить в моей реализации, что бы ускорить работу paging 3?


Ответы (0 шт):