Ten moduł Codelab jest częścią kursu Android Kotlin Fundamentals. Najwięcej korzyści przyniesie Ci ukończenie wszystkich ćwiczeń w kolejności. Wszystkie ćwiczenia z tego kursu znajdziesz na stronie docelowej kursu Android Kotlin Fundamentals.
Wprowadzenie
Prawie każda aplikacja na Androida, którą tworzysz, będzie w pewnym momencie wymagać połączenia z internetem. W tym i kolejnych modułach utworzysz aplikację, która łączy się z usługą internetową, aby pobierać i wyświetlać dane. Wykorzystasz też wiedzę zdobytą w ramach poprzednich ćwiczeń z programowania dotyczących ViewModel, LiveData i RecyclerView.
W tym ćwiczeniu w Codelabs do utworzenia warstwy sieciowej użyjesz bibliotek opracowanych przez społeczność. Znacznie upraszcza to pobieranie danych i obrazów, a także pomaga aplikacji spełniać niektóre sprawdzone metody Androida, takie jak wczytywanie obrazów w wątku w tle i zapisywanie wczytanych obrazów w pamięci podręcznej. W przypadku asynchronicznych lub nieblokujących sekcji kodu, takich jak komunikacja z warstwą usług internetowych, zmodyfikujesz aplikację, aby używała korutyn Kotlin. Zaktualizujesz też interfejs aplikacji, jeśli internet będzie działać wolno lub będzie niedostępny, aby poinformować użytkownika o tym, co się dzieje.
Co warto wiedzieć
- Jak tworzyć i używać fragmentów.
- Jak przechodzić między fragmentami i używać
safeArgsdo przekazywania danych między nimi. - Jak używać komponentów architektury, w tym transformacji
ViewModel,ViewModelProvider.Factory,LiveDataiLiveData. - Jak używać korutyn do długotrwałych zadań.
Czego się nauczysz
- Co to jest usługa internetowa REST.
- Korzystanie z biblioteki Retrofit do łączenia się z usługą internetową REST w internecie i uzyskiwania odpowiedzi.
- Używanie biblioteki Moshi do analizowania odpowiedzi JSON i przekształcania jej w obiekt danych.
Co musisz zrobić
- Zmodyfikuj aplikację startową, aby wysyłać żądania do interfejsu API usługi internetowej i obsługiwać odpowiedzi.
- Zaimplementuj w aplikacji warstwę sieciową za pomocą biblioteki Retrofit.
- Przeanalizuj odpowiedź JSON z usługi internetowej i przekształć ją w dane na żywo w aplikacji za pomocą biblioteki Moshi.
- Użyj obsługi korutyn w Retrofit, aby uprościć kod.
W tym samouczku (i w kolejnych) będziesz pracować z aplikacją startową MarsRealEstate, która wyświetla nieruchomości na sprzedaż na Marsie. Ta aplikacja łączy się z usługą internetową, aby pobierać i wyświetlać dane nieruchomości, w tym szczegóły takie jak cena oraz informacja, czy nieruchomość jest dostępna na sprzedaż lub wynajem. Obrazy przedstawiające poszczególne nieruchomości to prawdziwe zdjęcia z Marsa wykonane przez łaziki NASA.

Wersja aplikacji, którą utworzysz w tym laboratorium, nie będzie miała wielu elementów wizualnych. Skupia się ona na warstwie sieciowej aplikacji, która łączy się z internetem i pobiera surowe dane o nieruchomościach za pomocą usługi internetowej. Aby mieć pewność, że dane są prawidłowo pobierane i parsowane, wystarczy wydrukować liczbę obiektów na Marsie w widoku tekstowym:

.
Architektura aplikacji MarsRealEstate ma 2 główne moduły:
- Fragment z omówieniem, który zawiera siatkę miniatur zdjęć nieruchomości utworzoną za pomocą elementu
RecyclerView. - Fragment widoku szczegółowego zawierający informacje o każdej usłudze.

Aplikacja ma ViewModel dla każdego fragmentu. W tym laboratorium kodowania utworzysz warstwę dla usługi sieciowej, z którą ViewModel będzie się komunikować bezpośrednio. Jest to podobne do tego, co robiliśmy w poprzednich ćwiczeniach, gdy ViewModel komunikował się z bazą danych Room.
Widok ogólny ViewModel odpowiada za wysłanie wywołania sieciowego w celu uzyskania informacji o nieruchomościach na Marsie. Szczegóły ViewModel zawierają informacje o jednej nieruchomości na Marsie, która jest wyświetlana we fragmencie szczegółów. W przypadku każdego ViewModel używasz LiveData z powiązywaniem danych uwzględniającym cykl życia, aby aktualizować interfejs aplikacji, gdy dane się zmieniają.
Komponentu Navigation używasz zarówno do przechodzenia między 2 fragmentami, jak i do przekazywania wybranej usługi jako argumentu.
W tym zadaniu pobierzesz i uruchomisz aplikację początkową MarsRealEstate oraz zapoznasz się ze strukturą projektu.
Krok 1. Poznaj fragmenty i nawigację
- Pobierz aplikację startową MarsRealEstate i otwórz ją w Android Studio.
- Sprawdź
app/java/MainActivity.kt. Aplikacja używa fragmentów na obu ekranach, więc jedynym zadaniem aktywności jest wczytanie układu aktywności. - Sprawdź
app/res/layout/activity_main.xml. Układ aktywności jest hostem dla 2 fragmentów zdefiniowanych w pliku nawigacji. Ten układ tworzy instancję elementuNavHostFragmenti powiązanego z nim kontrolera nawigacji z zasobemnav_graph. - Otwórz pokój
app/res/navigation/nav_graph.xml. W tym miejscu możesz zobaczyć relację nawigacyjną między tymi 2 fragmentami. Wykres nawigacjiStartDestinationwskazujeoverviewFragment, więc fragment przeglądu jest tworzony podczas uruchamiania aplikacji.
Krok 2. Zapoznaj się z plikami źródłowymi Kotlin i powiązaniami danych
- W panelu Project (Projekt) rozwiń app > java. Zwróć uwagę, że aplikacja MarsRealEstate ma 3 foldery pakietów:
detail,networkioverview. Odpowiadają one 3 głównym komponentom aplikacji: fragmentom przeglądu i szczegółów oraz kodowi warstwy sieciowej.
- Otwórz pokój
app/java/overview/OverviewFragment.kt.OverviewFragmentleniwie inicjujeOverviewViewModel, co oznacza, żeOverviewViewModeljest tworzony przy pierwszym użyciu. - Sprawdź metodę
onCreateView(). Ta metoda powiększafragment_overviewukład za pomocą powiązania danych, ustawia właściciela cyklu życia powiązania na samą siebie (this) i ustawia zmiennąviewModelw obiekciebindingna samą siebie. Ponieważ ustawiliśmy właściciela cyklu życia, wszystkie obiektyLiveDataużywane w powiązaniu danych będą automatycznie obserwowane pod kątem zmian, a interfejs zostanie odpowiednio zaktualizowany. - Otwórz pokój
app/java/overview/OverviewViewModel. Odpowiedź jest typuLiveData, a my ustawiliśmy cykl życia zmiennej powiązania, więc wszelkie zmiany w niej będą aktualizować interfejs aplikacji. - Sprawdź blok
init. Po utworzeniu obiektuViewModelwywoływana jest metodagetMarsRealEstateProperties(). - Sprawdź metodę
getMarsRealEstateProperties(). W tej aplikacji startowej ta metoda zawiera odpowiedź zastępczą. Celem tego laboratorium jest zaktualizowanie odpowiedziLiveDatawViewModelza pomocą rzeczywistych danych pobranych z internetu. - Otwórz pokój
app/res/layout/fragment_overview.xml. Jest to układ fragmentu przeglądu, z którym będziesz pracować w tym laboratorium, i zawiera on powiązanie danych z modelem widoku. ImportujeOverviewViewModel, a następnie wiąże odpowiedź zViewModelzTextView. W dalszych ćwiczeniach zastąpisz widok tekstu siatką obrazów wRecyclerView. - Skompiluj i uruchom aplikację. W obecnej wersji aplikacji zobaczysz tylko odpowiedź początkową – „Set the Mars API Response here!” (Ustaw tutaj odpowiedź interfejsu Mars API!).
Dane dotyczące nieruchomości na Marsie są przechowywane na serwerze WWW jako usługa internetowa REST. Usługi internetowe korzystające z architektury REST są tworzone przy użyciu standardowych komponentów i protokołów internetowych.
Żądanie do usługi internetowej przesyłasz w standardowy sposób za pomocą identyfikatorów URI. Znany adres URL to w rzeczywistości typ identyfikatora URI, a w tym kursie oba te terminy są używane zamiennie. Na przykład w aplikacji z tej lekcji pobierasz wszystkie dane z tego serwera:
https://android-kotlin-fun-mars-server.appspot.com
Jeśli wpiszesz w przeglądarce ten adres URL, otrzymasz listę wszystkich dostępnych nieruchomości na Marsie.
https://android-kotlin-fun-mars-server.appspot.com/realestate
Odpowiedź usługi internetowej jest zwykle formatowana w JSON, czyli formacie wymiany danych do przedstawiania danych strukturalnych. Więcej informacji o JSON znajdziesz w następnym zadaniu. W skrócie: obiekt JSON to zbiór par klucz-wartość, czasami nazywany słownikiem, mapą mieszającą lub tablicą asocjacyjną. Zbiór obiektów JSON to tablica JSON, która jest zwracana jako odpowiedź z usługi internetowej.
Aby uzyskać te dane w aplikacji, musi ona nawiązać połączenie sieciowe i komunikować się z tym serwerem, a następnie odbierać i parsować dane odpowiedzi do formatu, którego może używać. W tym laboratorium kodowym użyjesz biblioteki klienta REST o nazwie Retrofit, aby nawiązać to połączenie.
Krok 1. Dodaj zależności Retrofit do Gradle
- Otwórz plik build.gradle (Module: app).
- W sekcji
dependenciesdodaj te wiersze dla bibliotek Retrofit:
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
Zwróć uwagę, że numery wersji są zdefiniowane osobno w pliku Gradle projektu. Pierwsza zależność dotyczy samej biblioteki Retrofit 2, a druga – konwertera skalarnego Retrofit. Ten konwerter umożliwia Retrofit zwracanie wyniku JSON jako String. Obie biblioteki współpracują ze sobą.
- Kliknij Synchronizuj teraz, aby ponownie utworzyć projekt z nowymi zależnościami.
Krok 2. Zaimplementuj MarsApiService
Retrofit tworzy interfejs API sieci dla aplikacji na podstawie treści z usługi internetowej. Pobiera dane z usługi internetowej i przekazuje je przez osobną bibliotekę konwertera, która wie, jak dekodować dane i zwracać je w postaci przydatnych obiektów. Retrofit ma wbudowaną obsługę popularnych formatów danych internetowych, takich jak XML i JSON. Retrofit tworzy większość warstwy sieciowej, w tym kluczowe szczegóły, takie jak uruchamianie żądań w wątkach w tle.
Klasa MarsApiService zawiera warstwę sieciową aplikacji, czyli interfejs API, którego ViewModel będzie używać do komunikacji z usługą internetową. W tej klasie zaimplementujesz interfejs API usługi Retrofit.
- Otwórz pokój
app/java/network/MarsApiService.kt. Obecnie plik zawiera tylko jedną rzecz: stałą dla podstawowego adresu URL usługi sieciowej.
private const val BASE_URL =
"https://android-kotlin-fun-mars-server.appspot.com"- Tuż pod tą stałą użyj kreatora Retrofit, aby utworzyć obiekt Retrofit. W razie potrzeby zaimportuj
retrofit2.Retrofitiretrofit2.converter.scalars.ScalarsConverterFactory.
private val retrofit = Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(BASE_URL)
.build()
Aby utworzyć interfejs API usług internetowych, Retrofit potrzebuje co najmniej 2 rzeczy: podstawowego identyfikatora URI usługi internetowej i fabryki konwerterów. Konwerter informuje bibliotekę Retrofit, co ma zrobić z danymi otrzymanymi z usługi internetowej. W tym przypadku chcesz, aby Retrofit pobrał odpowiedź JSON z usługi internetowej i zwrócił ją jako String. Retrofit ma ScalarsConverter, który obsługuje ciągi znaków i inne typy proste, więc w przypadku konstruktora wywołujesz addConverterFactory() z instancją ScalarsConverterFactory. Na koniec wywołaj build(), aby utworzyć obiekt Retrofit.
- Tuż pod wywołaniem konstruktora Retrofit zdefiniuj interfejs, który określa, w jaki sposób Retrofit komunikuje się z serwerem internetowym za pomocą żądań HTTP. Gdy pojawi się prośba, zaimportuj
retrofit2.http.GETiretrofit2.Call.
interface MarsApiService {
@GET("realestate")
fun getProperties():
Call<String>
}Obecnie celem jest uzyskanie ciągu znaków odpowiedzi JSON z usługi internetowej. Wystarczy do tego jedna metoda: getProperties(). Aby poinformować bibliotekę Retrofit, co ma robić ta metoda, użyj adnotacji @GET i określ ścieżkę lub punkt końcowy tej metody usługi internetowej. W tym przypadku punkt końcowy nazywa się realestate. Gdy wywoływana jest metoda getProperties(), Retrofit dołącza punkt końcowy realestate do adresu podstawowego (zdefiniowanego w konstruktorze Retrofit) i tworzy obiekt Call. Ten obiekt Call służy do rozpoczęcia żądania.
- Pod interfejsem
MarsApiServicezdefiniuj publiczny obiekt o nazwieMarsApi, aby zainicjować usługę Retrofit.
object MarsApi {
val retrofitService : MarsApiService by lazy {
retrofit.create(MarsApiService::class.java) }
}Metoda Retrofit create() tworzy samą usługę Retrofit z interfejsem MarsApiService. To wywołanie jest kosztowne, a aplikacja potrzebuje tylko jednej instancji usługi Retrofit, dlatego udostępniasz usługę reszcie aplikacji za pomocą publicznego obiektu o nazwie MarsApi i tam leniwie inicjujesz usługę Retrofit. Po zakończeniu konfiguracji za każdym razem, gdy aplikacja wywoła MarsApi.retrofitService, otrzyma obiekt Retrofit singleton, który implementuje MarsApiService.
Krok 3. Wywołaj usługę internetową w klasie OverviewViewModel
- Otwórz pokój
app/java/overview/OverviewViewModel.kt. Przewiń w dół do metodygetMarsRealEstateProperties().
private fun getMarsRealEstateProperties() {
_response.value = "Set the Mars API Response here!"
}To metoda, w której wywołasz usługę Retrofit i obsłużysz zwrócony ciąg JSON. Obecnie w odpowiedzi znajduje się tylko ciąg zastępczy.
- Usuń wiersz z tekstem zastępczym, który ustawia odpowiedź na „Set the Mars API Response here!” (Ustaw tutaj odpowiedź interfejsu Mars API).
- W
getMarsRealEstateProperties()dodaj kod widoczny poniżej. Gdy pojawi się prośba, zaimportujretrofit2.Callbackicom.example.android.marsrealestate.network.MarsApi.
MetodaMarsApi.retrofitService.getProperties()zwraca obiektCall. Następnie możesz wywołać na tym obiekcie funkcjęenqueue(), aby rozpocząć żądanie sieciowe w wątku w tle.
MarsApi.retrofitService.getProperties().enqueue(
object: Callback<String> {
})- Kliknij słowo
objectpodkreślone na czerwono. Kliknij Kod > Implementuj metody. Wybierz z listy zarównoonResponse(), jak ionFailure().
Android Studio dodaje kod z komentarzami TODO w każdej metodzie:
override fun onFailure(call: Call<String>, t: Throwable) {
TODO("not implemented")
}
override fun onResponse(call: Call<String>,
response: Response<String>) {
TODO("not implemented")
}- W
onFailure()usuń TODO i ustaw_responsena komunikat o błędzie, jak pokazano poniżej._responsetoLiveData, który określa, co jest wyświetlane w widoku tekstowym. Każdy stan musi zaktualizować_responseLiveData.
Wywołanie zwrotneonFailure()jest wywoływane, gdy odpowiedź usługi internetowej nie powiedzie się. W przypadku tej odpowiedzi ustaw stan_responsena"Failure: "połączony z wiadomością z argumentuThrowable.
override fun onFailure(call: Call<String>, t: Throwable) {
_response.value = "Failure: " + t.message
}- W
onResponse()usuń TODO i ustaw_responsena treść odpowiedzi. Wywołanie zwrotneonResponse()jest wywoływane, gdy żądanie zostanie zrealizowane, a usługa internetowa zwróci odpowiedź.
override fun onResponse(call: Call<String>,
response: Response<String>) {
_response.value = response.body()
}Krok 4. Określ uprawnienia internetowe
- Skompiluj i uruchom aplikację MarsRealEstate. Zwróć uwagę, że aplikacja natychmiast się zamyka z błędem.
- W Android Studio kliknij kartę Logcat i zwróć uwagę na błąd w logu, który zaczyna się od wiersza podobnego do tego:
Process: com.example.android.marsrealestate, PID: 10646 java.lang.SecurityException: Permission denied (missing INTERNET permission?)
Komunikat o błędzie informuje, że w aplikacji może brakować uprawnienia INTERNET. Połączenie z internetem wiąże się z zagrożeniami dla bezpieczeństwa, dlatego aplikacje domyślnie nie mają połączenia z internetem. Musisz wyraźnie poinformować Androida, że aplikacja potrzebuje dostępu do internetu.
- Otwórz pokój
app/manifests/AndroidManifest.xml. Dodaj ten wiersz tuż przed tagiem<application>:
<uses-permission android:name="android.permission.INTERNET" />- Skompiluj i uruchom aplikację ponownie. Jeśli połączenie internetowe działa prawidłowo, zobaczysz tekst JSON zawierający dane dotyczące Mars Property.

- Aby zamknąć aplikację, kliknij przycisk Wstecz na urządzeniu lub emulatorze.
- Włącz tryb samolotowy na urządzeniu lub emulatorze, a następnie ponownie otwórz aplikację z menu Ostatnie lub uruchom ją ponownie w Android Studio.

- Wyłącz ponownie tryb samolotowy.
Teraz otrzymujesz odpowiedź JSON z usługi internetowej Mars, co jest dobrym początkiem. Ale tak naprawdę potrzebujesz obiektów Kotlin, a nie długiego ciągu JSON. Istnieje biblioteka o nazwie Moshi, która jest parserem JSON na Androida, który przekształca ciąg znaków JSON w obiekty Kotlin. Retrofit ma konwerter, który współpracuje z Moshi, więc jest to świetna biblioteka do Twoich celów.
W tym zadaniu użyjesz biblioteki Moshi z Retrofit, aby przeanalizować odpowiedź JSON z usługi internetowej i przekształcić ją w przydatne obiekty Kotlin Mars Property. Zmieniasz aplikację tak, aby zamiast wyświetlać nieprzetworzony kod JSON, wyświetlała liczbę zwróconych obiektów Mars Properties.
Krok 1. Dodaj zależności biblioteki Moshi
- Otwórz plik build.gradle (Module: app).
- W sekcji zależności dodaj kod podany poniżej, aby uwzględnić zależności Moshi. Podobnie jak w przypadku Retrofit,
$version_moshijest definiowany oddzielnie w pliku Gradle na poziomie projektu. Te zależności dodają obsługę podstawowej biblioteki JSON Moshi i obsługę Kotlina w Moshi.
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"- Znajdź wiersz konwertera skalarnego Retrofit w bloku
dependencies:
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"- Zmień ten wiersz, aby używać
converter-moshi:
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"- Kliknij Synchronizuj teraz, aby ponownie utworzyć projekt z nowymi zależnościami.
Krok 2. Zaimplementuj klasę danych MarsProperty
Przykładowy wpis w odpowiedzi JSON otrzymanej z usługi internetowej wygląda tak:
[{"price":450000,
"id":"424906",
"type":"rent",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"},
...]Odpowiedź JSON pokazana powyżej to tablica, co wskazują nawiasy kwadratowe. Tablica zawiera obiekty JSON, które są otoczone nawiasami klamrowymi. Każdy obiekt zawiera zbiór par nazwa-wartość rozdzielonych dwukropkami. Nazwy są ujęte w cudzysłów. Wartości mogą być liczbami lub ciągami tekstowymi, a ciągi tekstowe są również ujęte w cudzysłów. Na przykład price dla tej nieruchomości wynosi 450 000 PLN, a img_src to adres URL, czyli lokalizacja pliku obrazu na serwerze.
W przykładzie powyżej każda pozycja dotycząca Marsa zawiera te pary klucz-wartość JSON:
price: cena nieruchomości na Marsie jako liczba.id: identyfikator usługi w postaci ciągu znaków.type:"rent"lub"buy".img_src: adres URL obrazu w postaci ciągu znaków.
Moshi analizuje te dane JSON i konwertuje je na obiekty Kotlin. W tym celu musi mieć klasę danych Kotlin do przechowywania przeanalizowanych wyników, więc następnym krokiem jest utworzenie tej klasy.
- Otwórz pokój
app/java/network/MarsProperty.kt. - Zastąp istniejącą definicję klasy
MarsPropertytym kodem:
data class MarsProperty(
val id: String, val img_src: String,
val type: String,
val price: Double
)Zwróć uwagę, że każda zmienna w klasie MarsProperty odpowiada nazwie klucza w obiekcie JSON. Aby dopasować typy w JSON, użyj obiektów String dla wszystkich wartości z wyjątkiem price, który jest Double. Wartość Double może reprezentować dowolną liczbę JSON.
Gdy Moshi analizuje JSON, dopasowuje klucze według nazwy i wypełnia obiekty danych odpowiednimi wartościami.
- Zastąp wiersz klawisza
img_srcwierszem pokazanym poniżej. W razie potrzeby zaimportujcom.squareup.moshi.Json.
@Json(name = "img_src") val imgSrcUrl: String,Czasami nazwy kluczy w odpowiedzi JSON mogą powodować niejasne właściwości Kotlin lub nie pasować do Twojego stylu kodowania. Na przykład w pliku JSON klucz img_src używa podkreślenia, podczas gdy właściwości Kotlin zwykle używają wielkich i małych liter („camel case”).
Aby używać w klasie danych nazw zmiennych, które różnią się od nazw kluczy w odpowiedzi JSON, użyj adnotacji @Json. W tym przykładzie nazwa zmiennej w klasie danych to imgSrcUrl. Zmienna jest mapowana na atrybut JSON img_src za pomocą funkcji @Json(name = "img_src").
Krok 3. Zaktualizuj MarsApiService i OverviewViewModel
Po utworzeniu klasy danych MarsProperty możesz zaktualizować interfejs API sieci i ViewModel, aby uwzględnić dane Moshi.
- Otwórz pokój
network/MarsApiService.kt. W przypadku elementuScalarsConverterFactorymogą się pojawić błędy dotyczące brakującej klasy. Wynika to ze zmiany zależności Retrofit wprowadzonej w kroku 1. Jak najszybciej napraw te błędy. - U góry pliku, tuż przed konstruktorem Retrofit, dodaj ten kod, aby utworzyć instancję Moshi. W razie potrzeby zaimportuj
com.squareup.moshi.Moshiicom.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory.
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()Podobnie jak w przypadku Retrofit, tutaj tworzysz obiekt moshi za pomocą kreatora Moshi. Aby adnotacje Moshi działały prawidłowo w przypadku języka Kotlin, dodaj KotlinJsonAdapterFactory, a następnie wywołaj build().
- Zmień narzędzie do tworzenia Retrofit, aby używać
MoshiConverterFactoryzamiastScalarConverterFactory, i przekaż utworzoną instancjęmoshi. W razie potrzeby zaimportujretrofit2.converter.moshi.MoshiConverterFactory.
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()- Usuń też import dla
ScalarConverterFactory.
Kod do usunięcia:
import retrofit2.converter.scalars.ScalarsConverterFactory- Zaktualizuj interfejs
MarsApiService, aby Retrofit zwracał listę obiektówMarsPropertyzamiastCall<String>.
interface MarsApiService {
@GET("realestate")
fun getProperties():
Call<List<MarsProperty>>
}- Otwórz pokój
OverviewViewModel.kt. Przewiń w dół do wywołania metodygetProperties().enqueue()w metodziegetMarsRealEstateProperties(). - Zmień argument na
enqueue()zCallback<String>naCallback<List<MarsProperty>>. W razie potrzeby zaimportujcom.example.android.marsrealestate.network.MarsProperty.
MarsApi.retrofitService.getProperties().enqueue(
object: Callback<List<MarsProperty>> {- W funkcji
onFailure()zmień argument zCall<String>naCall<List<MarsProperty>>:
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {- Wprowadź tę samą zmianę w obu argumentach funkcji
onResponse():
override fun onResponse(call: Call<List<MarsProperty>>,
response: Response<List<MarsProperty>>) {- W treści
onResponse()zastąp istniejące przypisanie do_response.valueprzypisaniem podanym poniżej. Ponieważresponse.body()jest teraz listą obiektówMarsProperty, rozmiar tej listy to liczba przeanalizowanych właściwości. Ta wiadomość z odpowiedzią zawiera liczbę właściwości:
_response.value =
"Success: ${response.body()?.size} Mars properties retrieved"- Upewnij się, że tryb samolotowy jest wyłączony. Skompiluj i uruchom aplikację. Tym razem w wiadomości powinna się wyświetlić liczba usług zwróconych przez usługę internetową:

Usługa interfejsu API Retrofit działa już, ale korzysta z wywołania zwrotnego z 2 metodami wywołania zwrotnego, które musisz zaimplementować. Jedna metoda obsługuje powodzenie, a druga – niepowodzenie. Wynik niepowodzenia zgłasza wyjątki. Kod byłby bardziej wydajny i czytelny, gdyby zamiast wywołań zwrotnych można było używać korutyn z obsługą wyjątków. Retrofit ma bibliotekę, która integruje korutyny.
W tym zadaniu przekształcisz usługę sieciową i ViewModel, aby korzystały z korutyn.
Krok 1. Dodaj zależności od procedur współbieżnych
- Otwórz plik build.gradle (Module: app).
- W sekcji zależności dodaj obsługę podstawowych bibliotek współprogramów Kotlin i biblioteki współprogramów Retrofit:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines" implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"
- Kliknij Synchronizuj teraz, aby ponownie utworzyć projekt z nowymi zależnościami.
Krok 2. Zaktualizuj MarsApiService i OverviewViewModel
- W
MarsApiService.ktzaktualizuj narzędzie do tworzenia Retrofit, aby używaćCoroutineCallAdapterFactory. Pełny kreator wygląda teraz tak:
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(BASE_URL)
.build()Adaptery wywołań umożliwiają Retrofitowi tworzenie interfejsów API, które zwracają coś innego niż domyślna klasa Call. W tym przypadku CoroutineCallAdapterFactory pozwala nam zastąpić obiekt Call, który zwraca getProperties(), obiektem Deferred.
- W metodzie
getProperties()zmieńCall<List<MarsProperty>>naDeferred<List<MarsProperty>>. W razie potrzeby zaimportujkotlinx.coroutines.Deferred. Pełna metodagetProperties()wygląda tak:
@GET("realestate")
fun getProperties():
Deferred<List<MarsProperty>>Interfejs Deferred definiuje zadanie korutyny, które zwraca wartość wyniku (Deferred dziedziczy z Job). Interfejs Deferred zawiera metodę o nazwie await(), która powoduje, że kod czeka bez blokowania, aż wartość będzie gotowa, a następnie zwraca tę wartość.
- Otwórz pokój
OverviewViewModel.kt. Tuż przed blokieminitdodaj zadanie korutyny:
private var viewModelJob = Job()- Utwórz zakres coroutine dla nowego zadania za pomocą głównego dyspozytora:
private val coroutineScope = CoroutineScope(
viewModelJob + Dispatchers.Main )Dispatchers.Main Dyspozytor używa wątku interfejsu do wykonywania swojej pracy. Retrofit wykonuje wszystkie działania w wątku w tle, więc nie ma potrzeby używania innego wątku w przypadku zakresu. Dzięki temu możesz łatwo zaktualizować wartość MutableLiveData, gdy uzyskasz wynik.
- Usuń cały kod w
getMarsRealEstateProperties(). Zamiast wywołania funkcjienqueue()oraz wywołań zwrotnychonFailure()ionResponse()użyjesz tutaj korutyn. - W funkcji
getMarsRealEstateProperties()uruchom korutynę:
coroutineScope.launch {
}
Aby użyć obiektu Deferred, który Retrofit zwraca w przypadku zadania sieciowego, musisz znajdować się w korutynie, więc tutaj uruchamiasz utworzoną korutynę. Kod jest nadal wykonywany w wątku głównym, ale teraz współbieżnością zarządzają korutyny.
- W bloku uruchamiania wywołaj funkcję
getProperties()na obiekcieretrofitService:
var getPropertiesDeferred = MarsApi.retrofitService.getProperties()Wywołanie funkcji getProperties() z usługi MarsApi tworzy i uruchamia połączenie sieciowe w wątku w tle, zwracając obiekt Deferred dla tego zadania.
- W bloku uruchamiania dodaj też blok
try/catch, aby obsługiwać wyjątki:
try {
} catch (e: Exception) {
}- W bloku
try {}wywołaj funkcjęawait()na obiekcieDeferred:
var listResult = getPropertiesDeferred.await()Wywołanie await() na obiekcie Deferred zwraca wynik wywołania sieciowego, gdy wartość jest gotowa. Metoda await() nie blokuje wątku, więc usługa Mars API pobiera dane z sieci bez blokowania bieżącego wątku, co jest ważne, ponieważ znajdujemy się w zakresie wątku interfejsu. Po wykonaniu zadania kod jest wykonywany dalej od miejsca, w którym został przerwany. Dzieje się to w bloku try {}, dzięki czemu możesz przechwytywać wyjątki.
- W bloku
try {}, po metodzieawait(), zaktualizuj wiadomość z odpowiedzią w przypadku pozytywnej odpowiedzi:
_response.value =
"Success: ${listResult.size} Mars properties retrieved"- W bloku
catch {}obsłuż odpowiedź o błędzie:
_response.value = "Failure: ${e.message}"
Pełna metoda getMarsRealEstateProperties() wygląda teraz tak:
private fun getMarsRealEstateProperties() {
coroutineScope.launch {
var getPropertiesDeferred =
MarsApi.retrofitService.getProperties()
try {
_response.value =
"Success: ${listResult.size} Mars properties retrieved"
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
}
}- U dołu klasy dodaj wywołanie zwrotne
onCleared()z tym kodem:
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}Wczytywanie danych powinno się zatrzymać, gdy ViewModel zostanie zniszczony, ponieważ OverviewFragment, który go używa, zniknie.ViewModel Aby zatrzymać ładowanie, gdy element ViewModel zostanie zniszczony, zastąp element onCleared(), aby anulować zadanie.
- Skompiluj i uruchom aplikację. Tym razem uzyskasz ten sam wynik co w poprzednim zadaniu (raport z liczbą właściwości), ale z bardziej przejrzystym kodem i obsługą błędów.
Projekt Android Studio: MarsRealEstateNetwork
Usługi internetowe REST
- Usługa internetowa to usługa w internecie, która umożliwia aplikacji wysyłanie żądań i otrzymywanie danych.
- Typowe usługi internetowe korzystają z architektury REST. Usługi internetowe, które oferują architekturę REST, są znane jako usługi RESTful. Usługi internetowe RESTful są tworzone przy użyciu standardowych komponentów i protokołów internetowych.
- Żądanie do usługi internetowej REST wysyłasz w standardowy sposób za pomocą identyfikatorów URI.
- Aby korzystać z usługi internetowej, aplikacja musi nawiązać połączenie sieciowe i komunikować się z nią. Następnie aplikacja musi otrzymać i przeanalizować dane odpowiedzi w formacie, którego może używać.
- Biblioteka Retrofit to biblioteka klienta, która umożliwia aplikacji wysyłanie żądań do usługi internetowej REST.
- Używaj konwerterów, aby określać, co Retrofit ma robić z danymi wysyłanymi do usługi internetowej i z niej odbieranymi. Na przykład konwerter
ScalarsConvertertraktuje dane usługi internetowej jakoStringlub inny typ prosty. - Aby umożliwić aplikacji łączenie się z internetem, dodaj uprawnienie
"android.permission.INTERNET"do manifestu Androida.
Analiza JSON
- Odpowiedź usługi internetowej jest często formatowana w JSON, czyli popularnym formacie wymiany danych strukturalnych.
- Obiekt JSON to zbiór par klucz-wartość. Ta kolekcja jest czasami nazywana słownikiem, mapą skrótów lub tablicą asocjacyjną.
- Zbiór obiektów JSON to tablica JSON. W odpowiedzi z usługi internetowej otrzymujesz tablicę JSON.
- Klucze w parze klucz-wartość są ujęte w cudzysłów. Wartości mogą być liczbami lub ciągami znaków. Ciągi znaków są również ujęte w cudzysłów.
- Biblioteka Moshi to parser JSON na Androida, który konwertuje ciąg JSON na obiekty Kotlin. Retrofit ma konwerter, który działa z Moshi.
- Moshi dopasowuje klucze w odpowiedzi JSON do właściwości w obiekcie danych, które mają tę samą nazwę.
- Aby użyć innej nazwy właściwości dla klucza, dodaj do tej właściwości adnotację
@Jsoni nazwę klucza JSON.
Retrofit i korutyny
- Adaptery wywołań umożliwiają Retrofitowi tworzenie interfejsów API, które zwracają coś innego niż domyślna klasa
Call. Użyj klasyCoroutineCallAdapterFactory, aby zastąpićCallkorutynąDeferred. - Użyj metody
await()na obiekcieDeferred, aby kod korutyny czekał bez blokowania, aż wartość będzie gotowa, a następnie zwróci tę wartość.
Kurs Udacity:
Dokumentacja dla deweloperów aplikacji na Androida:
Dokumentacja języka Kotlin:
- Ćwiczenia z programowania dotyczące współprogramów
- Korutyny, oficjalna dokumentacja
- Kontekst i dyspozytorzy korutyny
Inne:
W tej sekcji znajdziesz listę możliwych zadań domowych dla uczniów, którzy wykonują ten moduł w ramach kursu prowadzonego przez instruktora. Nauczyciel musi:
- W razie potrzeby przypisz pracę domową.
- Poinformuj uczniów, jak przesyłać projekty.
- Oceń zadania domowe.
Instruktorzy mogą korzystać z tych sugestii w dowolnym zakresie i mogą zadawać inne zadania domowe, które uznają za odpowiednie.
Jeśli wykonujesz ten kurs samodzielnie, możesz użyć tych zadań domowych, aby sprawdzić swoją wiedzę.
Odpowiedz na te pytania
Pytanie 1
Jakie 2 kluczowe elementy są potrzebne bibliotece Retrofit do utworzenia interfejsu API usług internetowych?
▢ Podstawowy identyfikator URI usługi internetowej i zapytanie GET.
▢ Podstawowy identyfikator URI usługi internetowej i fabryka konwerterów.
▢ Połączenie sieciowe z usługą internetową i token autoryzacji.
▢ fabryka konwerterów i parser odpowiedzi;
Pytanie 2
Do czego służy biblioteka Moshi?
▢ Aby odzyskać dane z usługi internetowej.
▢ Do interakcji z Retrofit w celu wysłania żądania usługi internetowej.
▢ Aby przeanalizować odpowiedź JSON z usługi internetowej i przekształcić ją w obiekty danych Kotlin.
▢ Aby zmienić nazwy obiektów Kotlin, tak aby pasowały do kluczy w odpowiedzi JSON.
Pytanie 3
Do czego służą adaptery połączeń Retrofit?
▢ Umożliwiają Retrofitowi korzystanie z korutyn.
▢ Przekształcają odpowiedź usługi internetowej w obiekty danych Kotlin.
▢ Zmieniają wywołanie Retrofit na wywołanie usługi internetowej.
▢ Umożliwiają zwracanie w Retrofit innych elementów niż domyślna klasa Call.
Rozpocznij kolejną lekcję:
Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z podstaw języka Kotlin na Androidzie.