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.