Omówienie biblioteki Paging 2 Zawiera Android Jetpack.

Biblioteka stron internetowych pomaga wczytywać i wyświetlać jednocześnie małe porcje danych. Wczytywanie częściowych danych na żądanie zmniejsza wykorzystanie przepustowości sieci i systemu i zasobami Google Cloud.

W tym przewodniku zawarto kilka przykładowych zastosowań biblioteki, a także jak to działa. Aby zobaczyć pełne przykłady, jak ta biblioteka , wypróbuj ćwiczenia w Codelabs i przykłady z dodatkowych .

Konfiguracja

Aby zaimportować komponenty stronicowania do aplikacji na Androida, dodaj te elementy zależności od pliku build.gradle aplikacji:

Odlotowe

dependencies {
  def paging_version = "2.1.2"

  implementation "androidx.paging:paging-runtime:$paging_version" // For Kotlin use paging-runtime-ktx

  // alternatively - without Android dependencies for testing
  testImplementation "androidx.paging:paging-common:$paging_version" // For Kotlin use paging-common-ktx

  // optional - RxJava support
  implementation "androidx.paging:paging-rxjava2:$paging_version" // For Kotlin use paging-rxjava2-ktx
}

Kotlin

dependencies {
  val paging_version = "2.1.2"

  implementation("androidx.paging:paging-runtime:$paging_version") // For Kotlin use paging-runtime-ktx

  // alternatively - without Android dependencies for testing
  testImplementation("androidx.paging:paging-common:$paging_version") // For Kotlin use paging-common-ktx

  // optional - RxJava support
  implementation("androidx.paging:paging-rxjava2:$paging_version") // For Kotlin use paging-rxjava2-ktx
}

Architektura bibliotek

W tej sekcji opisano i przedstawiliśmy główne komponenty biblioteki stronicowania.

Lista z podziałem na strony

Kluczowym komponentem biblioteki stron docelowych jest PagedList, która się wczytuje fragmenty danych o aplikacji, czy strony. Wraz ze wzrostem ilości potrzebnych danych stronę do istniejącego obiektu PagedList. Jeśli jakieś wczytane dane ulegną zmianie, zostanie utworzony nowy wystąpienie PagedList jest wysyłane do obserwowalnego posiadacza danych z LiveData lub obiekt oparty na RxJava2. Jako Liczba wygenerowanych obiektów: PagedList, interfejs aplikacji prezentuje jej zawartość z poszanowaniem cykle życia.

Poniższy fragment kodu pokazuje, jak skonfigurować model widoku danych w aplikacji wczytuj i prezentuj dane za pomocą posiadacza LiveData obiektów PagedList:

Kotlin

class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
    val concertList: LiveData<PagedList<Concert>> =
            concertDao.concertsByDate().toLiveData(pageSize = 50)
}

Java

public class ConcertViewModel extends ViewModel {
    private ConcertDao concertDao;
    public final LiveData<PagedList<Concert>> concertList;

    // Creates a PagedList object with 50 items per page.
    public ConcertViewModel(ConcertDao concertDao) {
        this.concertDao = concertDao;
        concertList = new LivePagedListBuilder<>(
                concertDao.concertsByDate(), 50).build();
    }
}

Dane

Wczytuje się każde wystąpienie instancji PagedList aktualnego podsumowania danych o aplikacji z odpowiadających im DataSource. Przepływy danych z backendu lub bazy danych aplikacji do obiektu PagedList.

W przykładzie poniżej użyto funkcji Trwałość sal lub bibliotekido porządkowania danych aplikacji. Jeśli chcesz przechowywać dane w inny sposób, możesz udostępnić własne dane, fabrykę źródeł.

Kotlin

@Dao
interface ConcertDao {
    // The Int type parameter tells Room to use a PositionalDataSource object.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    fun concertsByDate(): DataSource.Factory<Int, Concert>
}

Java

@Dao
public interface ConcertDao {
    // The Integer type parameter tells Room to use a
    // PositionalDataSource object.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    DataSource.Factory<Integer, Concert> concertsByDate();
}

Więcej informacji o wczytywaniu danych do obiektów PagedList znajdziesz tutaj: dowiesz się, jak wczytywać dane stronicowane.

Interfejs użytkownika

Zajęcia w PagedList współpracują z PagedListAdapter, aby wczytać elementy do RecyclerView Te razem podczas pobierania i wyświetlania treści lub animowanie treści.

Więcej informacji znajdziesz w przewodniku Wyświetlanie stron .

Obsługa różnych architektur danych

Biblioteka stron internetowych obsługuje te architektury danych:

  • Udostępniane tylko z serwera backendu.
  • Są przechowywane tylko w bazie danych na urządzeniu.
  • Kombinacja innych źródeł wykorzystująca bazę danych na urządzeniu jako pamięć podręczną.

Rysunek 1 pokazuje przepływ danych w każdym z tych scenariuszy dotyczących architektury. W w przypadku rozwiązania obejmującego tylko sieć lub tylko bazę danych dane przepływają bezpośrednio do do modelu jej interfejsu. Jeśli stosujesz podejście łączone, przepływy danych do bazy danych na urządzeniu, a potem do modelu interfejsu aplikacji. Co jakiś czas w punkcie końcowym przepływu danych brakuje danych do wczytania. żąda wtedy więcej danych od komponentu, który je dostarczył. Gdy na przykład w bazie danych na urządzeniu kończą się dane, żąda ona więcej danych z serwera.

Diagramy przepływów danych
. Rysunek 1. Jak dane przepływają przez każdą z architektur, Biblioteka stronnicza
.

W pozostałej części tej sekcji znajdziesz zalecenia dotyczące konfiguracji każdego i przypadku użycia przepływu danych.

Tylko sieć

Aby wyświetlić dane z serwera backendu, użyj synchronicznej wersji interfejsu API Retrofit API do załadowania informacje do własnych danych DataSource .

Tylko baza danych

Skonfiguruj urządzenie RecyclerView w celu obserwacji pamięci lokalnej, najlepiej stosując funkcję trwałości sal . Dzięki temu zawsze, gdy dane są wstawione lub zmodyfikowane w bazie danych aplikacji, zmiany te są automatycznie RecyclerView, w którym są wyświetlane te dane.

Sieć i baza danych

Po rozpoczęciu obserwowania bazy danych możesz nasłuchiwać, gdy w bazie danych brakuje danych za pomocą funkcji PagedList.BoundaryCallback Następnie możesz pobrać więcej elementów ze swojej sieci i wstawić je do w bazie danych. Jeśli interfejs użytkownika obserwuje bazę danych, to wystarczy.

Obsługa błędów sieci

Jeśli korzystasz z sieci do pobierania lub strony wyświetlanych danych za pomocą nie należy traktować sieci jako „dostępny” lub „niedostępny” przez cały czas, ponieważ wiele połączeń jest przerywanych lub niepewne:

  • Konkretny serwer może nie odpowiedzieć na żądanie sieciowe.
  • Urządzenie może być połączone z siecią, która jest wolna lub słaba.

Zamiast tego aplikacja powinna sprawdzać każdą prośbę pod kątem błędów i przywracać jak najbardziej bezproblemowo, gdy sieć jest niedostępna. Przykład: możesz spróbować ponownie, przycisk, który pozwala użytkownikom wybrać, czy krok odświeżenia danych nie działa. Jeśli podczas stronicowania danych wystąpi błąd, spróbuj ponownie żądania stronicowania.

Aktualizowanie istniejącej aplikacji

Jeśli aplikacja korzysta już z danych z bazy danych lub źródła backendu, możliwość bezpośredniego przejścia na funkcje dostępne w bibliotece stronicowania. Z tej sekcji dowiesz się, jak uaktualnić aplikację, która ma taki sam typowy wygląd.

Niestandardowe rozwiązania stronicowania

Jeśli używasz funkcji niestandardowych do wczytywania niewielkich podzbiorów danych ze strony źródła danych, możesz zastąpić tę logikę tą z zbioru danych PagedList. Instancje Usługa PagedList ma wbudowane połączenia z często używanymi źródłami danych. Te instancje a także adaptery RecyclerView obiektów, które które da się umieścić w interfejsie aplikacji.

Dane wczytywane z użyciem list zamiast stron

Jeśli jako zapasowej struktury danych interfejsu użytkownika używasz listy w pamięci adaptera, rozważ obserwację aktualizacji danych za pomocą PagedList, jeśli liczba Liczba elementów na liście może osiągnąć duży rozmiar. Instancje PagedList mogą używać: LiveData<PagedList> lub Observable<List>, aby przekazywać aktualizacje danych do interfejsu aplikacji, co minimalizuje czas wczytywania i wykorzystanie pamięci. Jeszcze lepiej, jeśli zastąpisz List z obiektem PagedList w aplikacji nie wymaga żadnych zmian strukturę interfejsu aplikacji lub funkcje aktualizacji danych.

Powiąż kursor danych z widokiem listy za pomocą CursorAdapter

Aplikacja może korzystać z CursorAdapter aby powiązać dane z Cursor ListView. W takim przypadku zazwyczaj trzeba aby przeprowadzić migrację z ListView do RecyclerView jako kontener interfejsu listy aplikacji, a następnie zastąp Cursor z komponentem Sala lub PositionalDataSource w zależności od tego, czy instancje Cursor mają dostęp do SQLite.

W niektórych sytuacjach, na przykład podczas pracy z wystąpieniami Spinner, dostarczasz tylko przejściówkę Biblioteka pobiera dane wczytane do adaptera, wyświetla dane za Ciebie. W takiej sytuacji zmień typ pliku danych adaptera do LiveData<PagedList>, a następnie zawijaj tę listę w obiekcie ArrayAdapter przed próbą użycia klasy biblioteki w zwiększaniu tych elementów w interfejsie.

Asynchroniczne ładowanie treści przy użyciu narzędzia AsyncListUtil

Jeśli używasz AsyncListUtil obiektów do umożliwia asynchroniczne ładowanie i wyświetlanie grup informacji, możesz łatwiej ładować dane:

  • Dane nie muszą decydować o pozycji. Biblioteka stronnicza umożliwia wczytywanie bezpośrednio z backendu za pomocą kluczy udostępnianych przez sieć.
  • Ilość danych może być niepoliczalna. Za pomocą biblioteki stronicowania można wczytać dane na stronach do momentu, gdy nie będą dostępne żadne dane.
  • Możesz łatwiej obserwować dane. Biblioteka stronnicza może prezentować: przechowywane przez model ViewModel aplikacji w obserwalnej strukturze danych.
.

Przykłady baz danych

Poniższe fragmenty kodu pokazują kilka możliwych sposobów utworzenia wszystkich nad ich współpracą.

Obserwacja danych podzielonych na strony przy użyciu LiveData

Fragment kodu poniżej przedstawia współdziałanie wszystkich elementów. Jako koncert zdarzenia w bazie danych zostały dodane, usunięte lub zmienione w bazie danych, RecyclerView to można aktualizować automatycznie i skutecznie:

Kotlin

@Dao
interface ConcertDao {
    // The Int type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    fun concertsByDate(): DataSource.Factory<Int, Concert>
}

class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
    val concertList: LiveData<PagedList<Concert>> =
            concertDao.concertsByDate().toLiveData(pageSize = 50)
}

class ConcertActivity : AppCompatActivity() {
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        val viewModel: ConcertViewModel by viewModels()
        val recyclerView = findViewById(R.id.concert_list)
        val adapter = ConcertAdapter()
        viewModel.concertList.observe(this, PagedList(adapter::submitList))
        recyclerView.setAdapter(adapter)
    }
}

class ConcertAdapter() :
        PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) {
    fun onBindViewHolder(holder: ConcertViewHolder, position: Int) {
        val concert: Concert? = getItem(position)

        // Note that "concert" is a placeholder if it's null.
        holder.bindTo(concert)
    }

    companion object {
        private val DIFF_CALLBACK = object :
                DiffUtil.ItemCallback<Concert>() {
            // Concert details may have changed if reloaded from the database,
            // but ID is fixed.
            override fun areItemsTheSame(oldConcert: Concert,
                    newConcert: Concert) = oldConcert.id == newConcert.id

            override fun areContentsTheSame(oldConcert: Concert,
                    newConcert: Concert) = oldConcert == newConcert
        }
    }
}

Java

@Dao
public interface ConcertDao {
    // The Integer type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    DataSource.Factory<Integer, Concert> concertsByDate();
}

public class ConcertViewModel extends ViewModel {
    private ConcertDao concertDao;
    public final LiveData<PagedList<Concert>> concertList;

    public ConcertViewModel(ConcertDao concertDao) {
        this.concertDao = concertDao;
        concertList = new LivePagedListBuilder<>(
            concertDao.concertsByDate(), /* page size */ 50).build();
    }
}

public class ConcertActivity extends AppCompatActivity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ConcertViewModel viewModel =
                new ViewModelProvider(this).get(ConcertViewModel.class);
        RecyclerView recyclerView = findViewById(R.id.concert_list);
        ConcertAdapter adapter = new ConcertAdapter();
        viewModel.concertList.observe(this, adapter::submitList);
        recyclerView.setAdapter(adapter);
    }
}

public class ConcertAdapter
        extends PagedListAdapter<Concert, ConcertViewHolder> {
    protected ConcertAdapter() {
        super(DIFF_CALLBACK);
    }

    @Override
    public void onBindViewHolder(@NonNull ConcertViewHolder holder,
            int position) {
        Concert concert = getItem(position);
        if (concert != null) {
            holder.bindTo(concert);
        } else {
            // Null defines a placeholder item - PagedListAdapter automatically
            // invalidates this row when the actual object is loaded from the
            // database.
            holder.clear();
        }
    }

    private static DiffUtil.ItemCallback<Concert> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<Concert>() {
        // Concert details may have changed if reloaded from the database,
        // but ID is fixed.
        @Override
        public boolean areItemsTheSame(Concert oldConcert, Concert newConcert) {
            return oldConcert.getId() == newConcert.getId();
        }

        @Override
        public boolean areContentsTheSame(Concert oldConcert,
                Concert newConcert) {
            return oldConcert.equals(newConcert);
        }
    };
}

Obserwacja danych stronicowanych za pomocą RxJava2

Jeśli wolisz używać RxJava2 zamiast LiveData, możesz zamiast tego utwórz obiekt Observable lub Flowable:

Kotlin

class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
    val concertList: Observable<PagedList<Concert>> =
            concertDao.concertsByDate().toObservable(pageSize = 50)
}

Java

public class ConcertViewModel extends ViewModel {
    private ConcertDao concertDao;
    public final Observable<PagedList<Concert>> concertList;

    public ConcertViewModel(ConcertDao concertDao) {
        this.concertDao = concertDao;

        concertList = new RxPagedListBuilder<>(
                concertDao.concertsByDate(), /* page size */ 50)
                        .buildObservable();
    }
}

Następnie możesz rozpocząć i przestać obserwować dane, korzystając z kodu w następujący sposób: snippet:

Kotlin

class ConcertActivity : AppCompatActivity() {
    private val adapter = ConcertAdapter()

    // Use the 'by viewModels()' Kotlin property delegate
    // from the activity-ktx artifact
    private val viewModel: ConcertViewModel by viewModels()

    private val disposable = CompositeDisposable()

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val recyclerView = findViewById(R.id.concert_list)
        recyclerView.setAdapter(adapter)
    }

    override fun onStart() {
        super.onStart()
        disposable.add(viewModel.concertList
                .subscribe(adapter::submitList)))
    }

    override fun onStop() {
        super.onStop()
        disposable.clear()
    }
}

Java

public class ConcertActivity extends AppCompatActivity {
    private ConcertAdapter adapter = new ConcertAdapter();
    private ConcertViewModel viewModel;

    private CompositeDisposable disposable = new CompositeDisposable();

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        RecyclerView recyclerView = findViewById(R.id.concert_list);

        viewModel = new ViewModelProvider(this).get(ConcertViewModel.class);
        recyclerView.setAdapter(adapter);
    }

    @Override
    protected void onStart() {
        super.onStart();
        disposable.add(viewModel.concertList
                .subscribe(adapter.submitList(flowableList)
        ));
    }

    @Override
    protected void onStop() {
        super.onStop();
        disposable.clear();
    }
}

Kody ConcertDao i ConcertAdapter są takie same dla Oparta na RxJava2 jak w przypadku rozwiązanie oparte na LiveData.

Prześlij opinię

Podziel się z nami swoimi opiniami i pomysłami, korzystając z tych zasobów:

Narzędzie do śledzenia błędów
Zgłoś problemy, abyśmy mogli je naprawić.

Dodatkowe materiały

Więcej informacji o bibliotece stronicowania znajdziesz w poniższe zasoby.

Próbki

Ćwiczenia z programowania

Filmy

. .