Questo codelab fa parte del corso Android Kotlin Fundamentals. Per ottenere il massimo valore da questo corso, ti consigliamo di seguire le codelab in sequenza. Tutti i codelab del corso sono elencati nella pagina di destinazione dei codelab Android Kotlin Fundamentals.
Introduzione
Quasi tutte le app per Android che crei dovranno connettersi a internet a un certo punto. In questo codelab e in quelli successivi, creerai un'app che si connette a un servizio web per recuperare e visualizzare i dati.  Inoltre, si basa su quanto appreso nei codelab precedenti su ViewModel, LiveData e RecyclerView. 
In questo codelab, utilizzerai librerie sviluppate dalla community per creare il livello di rete. Ciò semplifica notevolmente il recupero dei dati e delle immagini e aiuta anche l'app a rispettare alcune best practice di Android, come il caricamento delle immagini su un thread in background e la memorizzazione nella cache delle immagini caricate. Per le sezioni asincrone o non bloccanti all'interno del codice, ad esempio la comunicazione con il livello dei servizi web, modificherai l'app in modo che utilizzi le coroutine di Kotlin. Aggiornerai anche l'interfaccia utente dell'app se internet è lento o non disponibile per informare l'utente di cosa sta succedendo.
Cosa devi già sapere
- Come creare e utilizzare i frammenti.
 - Come spostarsi tra i fragment e utilizzare 
safeArgsper trasferire dati tra i fragment. - Come utilizzare i componenti dell'architettura, tra cui le trasformazioni 
ViewModel,ViewModelProvider.Factory,LiveDataeLiveData. - Come utilizzare le coroutine per attività di lunga durata.
 
Cosa imparerai a fare
- Che cos'è un servizio web REST.
 - Utilizzo della libreria Retrofit per connettersi a un servizio web REST su internet e ricevere una risposta.
 - Utilizzo della libreria Moshi per analizzare la risposta JSON in un oggetto dati.
 
Attività previste
- Modifica un'app iniziale per effettuare una richiesta API di servizio web e gestire la risposta.
 - Implementa un livello di rete per la tua app utilizzando la libreria Retrofit.
 - Analizza la risposta JSON del servizio web nei dati live della tua app con la libreria Moshi.
 - Utilizza il supporto di Retrofit per le coroutine per semplificare il codice.
 
In questo codelab (e in quelli successivi), lavorerai con un'app iniziale chiamata MarsRealEstate, che mostra le proprietà in vendita su Marte. Questa app si connette a un servizio web per recuperare e visualizzare i dati della proprietà, inclusi dettagli come il prezzo e se la proprietà è disponibile per la vendita o l'affitto. Le immagini che rappresentano ogni proprietà sono foto reali di Marte scattate dai rover marziani della NASA.

La versione dell'app che crei in questo codelab non avrà molti elementi visivi: si concentra sulla parte del livello di rete dell'app per connettersi a internet e scaricare i dati grezzi delle proprietà utilizzando un servizio web. Per assicurarti che i dati vengano recuperati e analizzati correttamente, stampa il numero di proprietà su Marte in una visualizzazione di testo:

.
L'architettura dell'app MarsRealEstate è composta da due moduli principali:
- Un frammento di panoramica, che contiene una griglia di immagini in miniatura delle proprietà, creato con un 
RecyclerView. - Un frammento della visualizzazione dettagliata, contenente informazioni su ogni proprietà.
 

L'app ha un ViewModel per ogni frammento. Per questo codelab, crei un livello per il servizio di rete e ViewModel comunica direttamente con questo livello di rete.  Questa procedura è simile a quella che hai eseguito nei codelab precedenti quando ViewModel comunicava con il database Room.
La panoramica ViewModel è responsabile della chiamata di rete per ottenere le informazioni sugli immobili di Marte. Il dettaglio ViewModel contiene i dettagli del singolo immobile su Marte visualizzato nel frammento di dettagli.  Per ogni ViewModel, utilizzi LiveData con il data binding sensibile al ciclo di vita per aggiornare la UI dell'app quando i dati cambiano.  
Utilizzi il componente Navigazione sia per spostarti tra i due frammenti sia per passare la proprietà selezionata come argomento.
In questa attività, scaricherai ed eseguirai l'app iniziale per MarsRealEstate e acquisirai familiarità con la struttura del progetto.
Passaggio 1: esplora i fragment e la navigazione
- Scarica l'app iniziale MarsRealEstate e aprila in Android Studio.
 - Esamina 
app/java/MainActivity.kt. L'app utilizza frammenti per entrambe le schermate, quindi l'unica attività per l'attività è caricare il layout dell'attività. - Esamina 
app/res/layout/activity_main.xml. Il layout dell'attività è l'host dei due fragment, definiti nel file di navigazione. Questo layout crea unNavHostFragmente il relativo controller di navigazione con la risorsanav_graph. - Apri 
app/res/navigation/nav_graph.xml. Qui puoi vedere la relazione di navigazione tra i due frammenti. Il grafico di navigazioneStartDestinationpunta aoverviewFragment, quindi il fragment di panoramica viene istanziato all'avvio dell'app. 
Passaggio 2: esplora i file sorgente Kotline il data binding
- Nel riquadro Progetto, espandi app > java. Tieni presente che l'app MarsRealEstate ha tre cartelle di pacchetti:  
detail,networkeoverview. Questi corrispondono ai tre componenti principali dell'app: i fragment di panoramica e dettagli e il codice per il livello di rete.
   - Apri 
app/java/overview/OverviewFragment.kt.OverviewFragmentinizializza in modo differitoOverviewViewModel, il che significa cheOverviewViewModelviene creato la prima volta che viene utilizzato. - Esamina il metodo 
onCreateView(). Questo metodo gonfia il layoutfragment_overviewutilizzando il data binding, imposta il proprietario del ciclo di vita del binding su se stesso (this) e imposta la variabileviewModelnell'oggettobinding. Poiché abbiamo impostato il proprietario del ciclo di vita, qualsiasiLiveDatautilizzato nel data binding verrà osservato automaticamente per eventuali modifiche e l'interfaccia utente verrà aggiornata di conseguenza. - Apri 
app/java/overview/OverviewViewModel. Poiché la risposta è unLiveDatae abbiamo impostato il ciclo di vita per la variabile di binding, qualsiasi modifica apportata aggiornerà la UI dell'app. - Esamina il blocco 
init. Quando viene creatoViewModel, chiama il metodogetMarsRealEstateProperties(). - Esamina il metodo 
getMarsRealEstateProperties(). In questa app iniziale, questo metodo contiene una risposta segnaposto. L'obiettivo di questo codelab è aggiornare la rispostaLiveDataall'interno diViewModelutilizzando dati reali ottenuti da internet. - Apri 
app/res/layout/fragment_overview.xml. Questo è il layout del fragment di panoramica con cui lavori in questo codelab e include il data binding per il view model. ImportaOverviewViewModele poi associa la risposta diViewModela unTextView. Nei codelab successivi, sostituirai la visualizzazione del testo con una griglia di immagini in unRecyclerView. - Compila ed esegui l'app. Nella versione attuale dell'app vedrai solo la risposta iniziale: "Set the Mars API Response here!" (Imposta qui la risposta dell'API Mars).
 
 
I dati immobiliari di Marte sono archiviati su un server web come servizio web REST. I servizi web che utilizzano l'architettura REST sono creati utilizzando componenti e protocolli web standard.
Effettui una richiesta a un servizio web in modo standardizzato tramite URI. L'URL web che conosciamo è in realtà un tipo di URI ed entrambi vengono utilizzati in modo intercambiabile in questo corso. Ad esempio, nell'app per questa lezione, recuperi tutti i dati dal seguente server:
https://android-kotlin-fun-mars-server.appspot.com
Se digiti il seguente URL nel browser, viene visualizzato un elenco di tutte le proprietà immobiliari disponibili su Marte.
https://android-kotlin-fun-mars-server.appspot.com/realestate
La risposta di un servizio web è generalmente formattata in JSON, un formato di scambio per rappresentare i dati strutturati. Scoprirai di più su JSON nella prossima attività, ma la spiegazione breve è che un oggetto JSON è una raccolta di coppie chiave-valore, a volte chiamata dizionario, hash map o array associativo. Una raccolta di oggetti JSON è un array JSON ed è l'array che ricevi come risposta da un servizio web.
Per inserire questi dati nell'app, l'app deve stabilire una connessione di rete e comunicare con il server, quindi ricevere e analizzare i dati di risposta in un formato utilizzabile dall'app. In questo codelab, utilizzi una libreria client REST chiamata Retrofit per stabilire questa connessione.
Passaggio 1: aggiungi le dipendenze Retrofit a Gradle
- Apri build.gradle (Module: app).
 - Nella sezione 
dependencies, aggiungi queste righe per le librerie Retrofit: 
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
Tieni presente che i numeri di versione sono definiti separatamente nel file Gradle del progetto. La prima dipendenza riguarda la libreria Retrofit 2 stessa, mentre la seconda riguarda il convertitore scalare Retrofit.  Questo convertitore consente a Retrofit di restituire il risultato JSON come String. Le due librerie funzionano insieme.
- Fai clic su Sincronizza ora per ricompilare il progetto con le nuove dipendenze.
 
Passaggio 2: implementa MarsApiService
Retrofit crea un'API di rete per l'app in base ai contenuti del servizio web. Recupera i dati dal servizio web e li indirizza tramite una libreria di conversione separata che sa come decodificarli e restituirli sotto forma di oggetti utili. Retrofit include il supporto integrato per i formati di dati web più diffusi, come XML e JSON. Retrofit crea la maggior parte del livello di rete, inclusi dettagli critici come l'esecuzione delle richieste sui thread in background.
La classe MarsApiService contiene il livello di rete per l'app, ovvero l'API che ViewModel utilizzerà per comunicare con il servizio web.  Questa è la classe in cui implementerai l'API del servizio Retrofit.  
- Apri 
app/java/network/MarsApiService.kt. Al momento il file contiene una sola cosa: una costante per l'URL di base del servizio web. 
private const val BASE_URL = 
   "https://android-kotlin-fun-mars-server.appspot.com"- Appena sotto questa costante, utilizza un builder Retrofit per creare un oggetto Retrofit.  Importa 
retrofit2.Retrofiteretrofit2.converter.scalars.ScalarsConverterFactoryquando richiesto. 
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()
Retrofit ha bisogno di almeno due elementi per creare un'API di servizi web: l'URI di base per il servizio web e una factory di conversione.  Il convertitore indica a Retrofit cosa fare con i dati che riceve dal servizio web.  In questo caso, vuoi che Retrofit recuperi una risposta JSON dal servizio web e la restituisca come String.  Retrofit ha un ScalarsConverter che supporta stringhe e altri tipi primitivi, quindi chiami addConverterFactory() sul builder con un'istanza di ScalarsConverterFactory.  Infine, chiami build() per creare l'oggetto Retrofit.
- Subito sotto la chiamata al builder Retrofit, definisci un'interfaccia che definisce il modo in cui Retrofit comunica con il server web utilizzando le richieste HTTP.  Importa 
retrofit2.http.GETeretrofit2.Callquando richiesto. 
interface MarsApiService {
    @GET("realestate")
    fun getProperties():
            Call<String>
}Al momento, l'obiettivo è ottenere la stringa di risposta JSON dal servizio web e hai bisogno di un solo metodo per farlo: getProperties().  Per indicare a Retrofit cosa deve fare questo metodo, utilizza un'annotazione @GET e specifica il percorso o l'endpoint per quel metodo del servizio web.  In questo caso, l'endpoint si chiama realestate. Quando viene richiamato il metodo getProperties(), Retrofit aggiunge l'endpoint realestate all'URL di base (che hai definito nel builder Retrofit) e crea un oggetto Call. L'oggetto Call viene utilizzato per avviare la richiesta.
- Sotto l'interfaccia 
MarsApiService, definisci un oggetto pubblico chiamatoMarsApiper inizializzare il servizio Retrofit. 
object MarsApi {
    val retrofitService : MarsApiService by lazy { 
       retrofit.create(MarsApiService::class.java) }
}Il metodo Retrofit create() crea il servizio Retrofit stesso con l'interfaccia MarsApiService.  Poiché questa chiamata è costosa e l'app ha bisogno di una sola istanza del servizio Retrofit, esponi il servizio al resto dell'app utilizzando un oggetto pubblico chiamato MarsApi e inizializza il servizio Retrofit in modo differito. Ora che la configurazione è completata, ogni volta che la tua app chiama MarsApi.retrofitService,  riceverà un oggetto Retrofit singleton che implementa MarsApiService.
Passaggio 3: chiama il servizio web in OverviewViewModel
- Apri 
app/java/overview/OverviewViewModel.kt. Scorri verso il basso fino al metodogetMarsRealEstateProperties(). 
private fun getMarsRealEstateProperties() {
   _response.value = "Set the Mars API Response here!"
}Questo è il metodo in cui chiamerai il servizio Retrofit e gestirai la stringa JSON restituita. Al momento è presente solo una stringa segnaposto per la risposta.
- Elimina la riga segnaposto che imposta la risposta su "Set the Mars API Response here!" (Imposta qui la risposta dell'API Mars).
 - All'interno di 
getMarsRealEstateProperties(), aggiungi il codice mostrato di seguito. Importaretrofit2.Callbackecom.example.android.marsrealestate.network.MarsApiquando richiesto.
Il metodoMarsApi.retrofitService.getProperties()restituisce un oggettoCall. Poi puoi chiamareenqueue()su quell'oggetto per avviare la richiesta di rete su un thread in background. 
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<String> {
})- Fai clic sulla parola 
object, sottolineata in rosso. Seleziona Codice > Implementa metodi. Seleziona siaonResponse()cheonFailure()dall'elenco.
Android Studio aggiunge il codice con i TODO in ogni metodo: 
override fun onFailure(call: Call<String>, t: Throwable) {
       TODO("not implemented") 
}
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
       TODO("not implemented") 
}- In 
onFailure(), elimina TODO e imposta_responsesu un messaggio di errore, come mostrato di seguito._responseè una stringaLiveDatache determina cosa viene mostrato nella visualizzazione di testo. Ogni stato deve aggiornare_responseLiveData.
Il callbackonFailure()viene chiamato quando la risposta del servizio web non va a buon fine. Per questa risposta, imposta lo stato_responsesu"Failure: "concatenato al messaggio dell'argomentoThrowable. 
override fun onFailure(call: Call<String>, t: Throwable) {
   _response.value = "Failure: " + t.message
}- In 
onResponse(), elimina il TODO e imposta_responsesul corpo della risposta. Il callbackonResponse()viene chiamato quando la richiesta ha esito positivo e il servizio web restituisce una risposta. 
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
      _response.value = response.body()
}Passaggio 4: definisci l'autorizzazione a internet
- Compila ed esegui l'app MarsRealEstate.  Tieni presente che l'app si chiude immediatamente con un errore. 
   - Fai clic sulla scheda Logcat in Android Studio e prendi nota dell'errore nel log, che inizia con una riga simile a questa:
 
Process: com.example.android.marsrealestate, PID: 10646 java.lang.SecurityException: Permission denied (missing INTERNET permission?)
Il messaggio di errore indica che alla tua app potrebbe mancare l'autorizzazione INTERNET. La connessione a internet introduce problemi di sicurezza, motivo per cui le app non hanno la connettività a internet per impostazione predefinita. Devi comunicare esplicitamente ad Android che l'app deve accedere a internet.
- Apri 
app/manifests/AndroidManifest.xml. Aggiungi questa riga appena prima del tag<application>: 
<uses-permission android:name="android.permission.INTERNET" />- Compila ed esegui di nuovo l'app.  Se tutto funziona correttamente con la tua connessione a internet, vedrai il testo JSON contenente i dati della proprietà Mars.

 - Tocca il pulsante Indietro sul dispositivo o nell'emulatore per chiudere l'app.
 - Metti il dispositivo o l'emulatore in modalità aereo, quindi riapri l'app dal menu Recenti o riavviala da Android Studio.
 

- Disattiva di nuovo la modalità aereo.
 
Ora ricevi una risposta JSON dal servizio web Mars, il che è un ottimo inizio. Ma quello che ti serve davvero sono oggetti Kotlin, non una grande stringa JSON. Esiste una libreria chiamata Moshi, un parser JSON per Android che converte una stringa JSON in oggetti Kotlin. Retrofit ha un convertitore che funziona con Moshi, quindi è un'ottima libreria per i tuoi scopi.
In questa attività, utilizzerai la libreria Moshi con Retrofit per analizzare la risposta JSON del servizio web in utili oggetti Kotlin di Mars Property. Modifichi l'app in modo che, anziché visualizzare il JSON non elaborato, mostri il numero di proprietà di Marte restituite.
Passaggio 1: aggiungi le dipendenze della libreria Moshi
- Apri build.gradle (Module: app).
 - Nella sezione delle dipendenze, aggiungi il codice mostrato di seguito per includere le dipendenze di Moshi. Come per Retrofit, 
$version_moshiè definito separatamente nel file Gradle a livello di progetto. Queste dipendenze aggiungono il supporto per la libreria JSON principale di Moshi e per il supporto di Kotlin di Moshi. 
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"- Individua la riga per il convertitore scalare Retrofit nel blocco 
dependencies: 
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"- Modifica la riga in modo che utilizzi 
converter-moshi: 
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"- Fai clic su Sincronizza ora per ricompilare il progetto con le nuove dipendenze.
 
Passaggio 2: implementa la classe di dati MarsProperty
Una voce di esempio della risposta JSON che ricevi dal servizio web è simile alla seguente:
[{"price":450000,
"id":"424906",
"type":"rent",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"},
...]La risposta JSON mostrata sopra è un array, indicato dalle parentesi quadre.  L'array contiene oggetti JSON, racchiusi tra parentesi graffe.  Ogni oggetto contiene una serie di coppie nome-valore, separate da due punti. I nomi sono racchiusi tra virgolette. I valori possono essere numeri o stringhe e le stringhe sono racchiuse tra virgolette. Ad esempio, il price per questa proprietà è di 450.000 $e il img_src è un URL, ovvero la posizione del file immagine sul server. 
Nell'esempio precedente, nota che ogni voce della proprietà Mars ha queste coppie chiave-valore JSON:
price: il prezzo della proprietà di Marte, come numero.id: l'ID della proprietà, come stringa.type:"rent"o"buy".img_src: l'URL dell'immagine come stringa.
Moshi analizza questi dati JSON e li converte in oggetti Kotlin. Per farlo, deve avere una classe di dati Kotlin per archiviare i risultati analizzati, quindi il passaggio successivo è creare questa classe.
- Apri 
app/java/network/MarsProperty.kt. - Sostituisci la definizione della classe 
MarsPropertyesistente con il seguente codice: 
data class MarsProperty(
   val id: String, val img_src: String,
   val type: String,
   val price: Double
)Tieni presente che ogni variabile della classe MarsProperty corrisponde a un nome di chiave nell'oggetto JSON.  Per corrispondere ai tipi nel JSON, utilizzi oggetti String per tutti i valori tranne price, che è un Double.  Un Double può essere utilizzato per rappresentare qualsiasi numero JSON.
Quando Moshi analizza il JSON, associa le chiavi per nome e riempie gli oggetti dati con i valori appropriati.
- Sostituisci la riga per il tasto 
img_srccon la riga mostrata di seguito. Importacom.squareup.moshi.Jsonquando richiesto. 
@Json(name = "img_src") val imgSrcUrl: String,A volte i nomi delle chiavi in una risposta JSON possono creare proprietà Kotlin confuse o non corrispondere al tuo stile di codifica. Ad esempio, nel file JSON la chiave img_src utilizza un trattino basso, mentre le proprietà Kotlin utilizzano comunemente lettere maiuscole e minuscole ("camel case"). 
Per utilizzare nomi di variabili nella classe di dati diversi dai nomi delle chiavi nella risposta JSON, utilizza l'annotazione @Json. In questo esempio, il nome della variabile nella classe di dati è imgSrcUrl. La variabile viene mappata all'attributo JSON img_src utilizzando @Json(name = "img_src").
Passaggio 3: aggiorna MarsApiService e OverviewViewModel
Con la classe di dati MarsProperty in posizione, ora puoi aggiornare l'API di rete e ViewModel per includere i dati Moshi.  
- Apri 
network/MarsApiService.kt. Potresti visualizzare errori di classe mancante perScalarsConverterFactory. Ciò è dovuto alla modifica della dipendenza Retrofit apportata nel passaggio 1. Correggi questi errori al più presto. - Nella parte superiore del file, appena prima del builder Retrofit, aggiungi il seguente codice per creare l'istanza Moshi.  Importa 
com.squareup.moshi.Moshiecom.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactoryquando richiesto. 
private val moshi = Moshi.Builder()
   .add(KotlinJsonAdapterFactory())
   .build()Come hai fatto con Retrofit, qui crei un oggetto moshi utilizzando il builder Moshi.  Affinché le annotazioni di Moshi funzionino correttamente con Kotlin, aggiungi KotlinJsonAdapterFactory e poi chiama build().  
- Modifica il generatore Retrofit in modo che utilizzi 
MoshiConverterFactoryanzichéScalarConverterFactorye passa l'istanzamoshiche hai appena creato. Importaretrofit2.converter.moshi.MoshiConverterFactoryquando richiesto. 
private val retrofit = Retrofit.Builder()
   .addConverterFactory(MoshiConverterFactory.create(moshi))
   .baseUrl(BASE_URL)
   .build()- Elimina anche l'importazione per 
ScalarConverterFactory. 
Codice da eliminare:
import retrofit2.converter.scalars.ScalarsConverterFactory- Aggiorna l'interfaccia 
MarsApiServicein modo che Retrofit restituisca un elenco di oggettiMarsPropertyanzichéCall<String>. 
interface MarsApiService {
   @GET("realestate")
   fun getProperties():
      Call<List<MarsProperty>>
}- Apri 
OverviewViewModel.kt. Scorri verso il basso fino alla chiamata agetProperties().enqueue()nel metodogetMarsRealEstateProperties(). - Modifica l'argomento in 
enqueue()daCallback<String>aCallback<List<MarsProperty>>. Importacom.example.android.marsrealestate.network.MarsPropertyquando richiesto. 
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<List<MarsProperty>> {- In 
onFailure(), modifica l'argomento daCall<String>aCall<List<MarsProperty>>: 
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {- Apporta la stessa modifica a entrambi gli argomenti di 
onResponse(): 
override fun onResponse(call: Call<List<MarsProperty>>, 
   response: Response<List<MarsProperty>>) {- Nel corpo di 
onResponse(), sostituisci l'assegnazione esistente a_response.valuecon l'assegnazione mostrata di seguito. Poichéresponse.body()ora è un elenco di oggettiMarsProperty, la dimensione di questo elenco è il numero di proprietà analizzate. Questo messaggio di risposta stampa il numero di proprietà: 
_response.value = 
   "Success: ${response.body()?.size} Mars properties retrieved"- Assicurati che la modalità aereo sia disattivata.  Compila ed esegui l'app. Questa volta il messaggio dovrebbe mostrare il numero di proprietà restituite dal servizio web:

 
Ora il servizio API Retrofit è in esecuzione, ma utilizza un callback con due metodi di callback che devi implementare. Un metodo gestisce l'esito positivo e un altro gestisce l'esito negativo, mentre il risultato negativo segnala le eccezioni. Il tuo codice sarebbe più efficiente e più facile da leggere se potessi utilizzare le coroutine con la gestione delle eccezioni, anziché utilizzare i callback. Retrofit ha una libreria che integra le coroutine.
In questa attività, converti il servizio di rete e ViewModel per utilizzare le coroutine.  
Passaggio 1: aggiungi le dipendenze delle coroutine
- Apri build.gradle (Module: app).
 - Nella sezione delle dipendenze, aggiungi il supporto per le librerie di coroutine Kotlin di base e per la libreria di coroutine 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"
- Fai clic su Sincronizza ora per ricompilare il progetto con le nuove dipendenze.
 
Passaggio 2: aggiorna MarsApiService e OverviewViewModel
- In 
MarsApiService.kt, aggiorna il builder Retrofit per utilizzareCoroutineCallAdapterFactory. Il builder completo ora ha questo aspetto: 
private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .baseUrl(BASE_URL)
        .build()Gli adattatori di chiamata aggiungono la possibilità a Retrofit di creare API che restituiscono un valore diverso dalla classe Call predefinita.  In questo caso, CoroutineCallAdapterFactory ci consente di sostituire l'oggetto Call restituito da getProperties() con un oggetto Deferred.
- Nel metodo 
getProperties(), modificaCall<List<MarsProperty>>inDeferred<List<MarsProperty>>. Importakotlinx.coroutines.Deferredquando richiesto. Il metodogetProperties()completo ha il seguente aspetto: 
@GET("realestate")
fun getProperties():
   Deferred<List<MarsProperty>>L'interfaccia Deferred definisce un job di coroutine che restituisce un valore di risultato (Deferred eredita da Job). L'interfaccia Deferred include un metodo chiamato await(), che fa sì che il codice attenda senza bloccarsi finché il valore non è pronto, dopodiché viene restituito. 
- Apri 
OverviewViewModel.kt. Poco prima del bloccoinit, aggiungi un job di coroutine: 
private var viewModelJob = Job()- Crea un ambito di coroutine per il nuovo job utilizzando il dispatcher principale:
 
private val coroutineScope = CoroutineScope(
   viewModelJob + Dispatchers.Main )Il dispatcher Dispatchers.Main utilizza il thread UI per il suo lavoro.  Poiché Retrofit esegue tutto il suo lavoro su un thread in background, non c'è motivo di utilizzare un altro thread per l'ambito. In questo modo puoi aggiornare facilmente il valore di MutableLiveData quando ottieni un risultato.
- Elimina tutto il codice all'interno di 
getMarsRealEstateProperties(). Qui utilizzerai le coroutine anziché la chiamata aenqueue()e i callbackonFailure()eonResponse(). - All'interno di 
getMarsRealEstateProperties(), avvia la coroutine: 
coroutineScope.launch { 
}
Per utilizzare l'oggetto Deferred restituito da Retrofit per l'attività di rete, devi trovarti all'interno di una coroutine, quindi qui avvii la coroutine che hai appena creato.  Continui a eseguire il codice sul thread principale, ma ora lasci che le coroutine gestiscano la concorrenza.
- All'interno del blocco di avvio, chiama 
getProperties()sull'oggettoretrofitService: 
var getPropertiesDeferred = MarsApi.retrofitService.getProperties()La chiamata di getProperties() dal servizio MarsApi crea e avvia la chiamata di rete su un thread in background, restituendo l'oggetto Deferred per l'attività. 
- All'interno del blocco di avvio, aggiungi anche un blocco 
try/catchper gestire le eccezioni: 
try {
} catch (e: Exception) {
  
}- All'interno del blocco 
try {}, chiamaawait()sull'oggettoDeferred: 
var listResult = getPropertiesDeferred.await()La chiamata di await() sull'oggetto Deferred restituisce il risultato della chiamata di rete quando il valore è pronto. Il metodo await() non è bloccante, quindi il servizio API Mars recupera i dati dalla rete senza bloccare il thread corrente, il che è importante perché ci troviamo nell'ambito del thread UI. Una volta completata l'attività, l'esecuzione del codice continua da dove era stata interrotta.  Questo avviene all'interno di try {}, in modo da poter rilevare le eccezioni.  
- Inoltre, all'interno del blocco 
try {}, dopo il metodoawait(), aggiorna il messaggio di risposta per la risposta riuscita: 
_response.value = 
   "Success: ${listResult.size} Mars properties retrieved"- All'interno del blocco 
catch {}, gestisci la risposta di errore: 
_response.value = "Failure: ${e.message}"
Il metodo getMarsRealEstateProperties() completo ora ha questo aspetto:
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}"
       }
   }
}- In fondo alla classe, aggiungi un callback 
onCleared()con questo codice: 
override fun onCleared() {
   super.onCleared()
   viewModelJob.cancel()
}Il caricamento dei dati deve interrompersi quando ViewModel viene eliminato, perché OverviewFragment che lo utilizza non sarà più disponibile.ViewModel Per interrompere il caricamento quando ViewModel viene eliminato, esegui l'override di onCleared() per annullare il job.
- Compila ed esegui l'app. Questa volta ottieni lo stesso risultato dell'attività precedente (un report sul numero di proprietà), ma con un codice e una gestione degli errori più semplici.
 
Progetto Android Studio: MarsRealEstateNetwork
Servizi web REST
- Un servizio web è un servizio su internet che consente alla tua app di effettuare richieste e ricevere dati.
 - I servizi web comuni utilizzano un'architettura REST. I servizi web che offrono l'architettura REST sono noti come servizi RESTful. I servizi web RESTful sono creati utilizzando componenti e protocolli web standard.
 - Effettui una richiesta a un servizio web REST in modo standardizzato, tramite URI.
 - Per utilizzare un servizio web, un'app deve stabilire una connessione di rete e comunicare con il servizio. Quindi, l'app deve ricevere e analizzare i dati delle risposte in un formato utilizzabile.
 - La libreria Retrofit è una libreria client che consente alla tua app di effettuare richieste a un servizio web REST.
 - Utilizza i converter per indicare a Retrofit cosa fare con i dati che invia al servizio web e che riceve dal servizio web.  Ad esempio, il convertitore 
ScalarsConverterconsidera i dati del servizio web come unStringo un altro tipo primitivo. - Per consentire alla tua app di connettersi a internet, aggiungi l'autorizzazione 
"android.permission.INTERNET"nel manifest Android. 
Analisi JSON
- La risposta di un servizio web è spesso formattata in JSON, un formato di scambio comune per rappresentare i dati strutturati.
 - Un oggetto JSON è una raccolta di coppie chiave-valore. Questa raccolta viene a volte chiamata dizionario, hash map o array associativo.
 - Una raccolta di oggetti JSON è un array JSON. Ricevi un array JSON come risposta da un servizio web.
 - Le chiavi di una coppia chiave-valore sono racchiuse tra virgolette. I valori possono essere numeri o stringhe. Anche le stringhe sono racchiuse tra virgolette.
 - La libreria Moshi è un parser JSON per Android che converte una stringa JSON in oggetti Kotlin. Retrofit ha un convertitore che funziona con Moshi.
 - Moshi associa le chiavi in una risposta JSON alle proprietà di un oggetto dati con lo stesso nome.
 - Per utilizzare un nome di proprietà diverso per una chiave, annota la proprietà con l'annotazione 
@Jsone il nome della chiave JSON. 
Retrofit e coroutine
- Gli adattatori di chiamata consentono a Retrofit di creare API che restituiscono un valore diverso dalla classe 
Callpredefinita. Utilizza la classeCoroutineCallAdapterFactoryper sostituireCallcon una coroutineDeferred. - Utilizza il metodo 
await()sull'oggettoDeferredper fare in modo che il codice della coroutine attenda senza bloccarsi finché il valore non è pronto, dopodiché il valore viene restituito. 
Corso Udacity:
Documentazione per sviluppatori Android:
Documentazione di Kotlin:
Altro:
Questa sezione elenca i possibili compiti a casa per gli studenti che seguono questo codelab nell'ambito di un corso guidato da un insegnante. Spetta all'insegnante:
- Assegna i compiti, se richiesto.
 - Comunica agli studenti come inviare i compiti.
 - Valuta i compiti a casa.
 
Gli insegnanti possono utilizzare questi suggerimenti nella misura che ritengono opportuna e sono liberi di assegnare qualsiasi altro compito a casa che ritengono appropriato.
Se stai seguendo questo codelab in autonomia, sentiti libero di utilizzare questi compiti per casa per mettere alla prova le tue conoscenze.
Rispondi a queste domande
Domanda 1
Quali sono i due elementi chiave necessari a Retrofit per creare un'API di servizi web?
▢ L'URI di base per il servizio web e una query GET.
▢ L'URI di base per il servizio web e una factory di conversione.
▢ Una connessione di rete al servizio web e un token di autorizzazione.
▢ Una fabbrica di convertitori e un parser per la risposta.
Domanda 2
A cosa serve la libreria Moshi?
▢ Per recuperare i dati da un servizio web.
▢ Per interagire con Retrofit per effettuare una richiesta di servizio web.
▢ Per analizzare una risposta JSON da un servizio web in oggetti dati Kotlin.
▢ Per rinominare gli oggetti Kotlin in modo che corrispondano alle chiavi nella risposta JSON.
Domanda 3
A cosa servono gli adattatori di chiamata retrofit?
▢ Consentono a Retrofit di utilizzare le coroutine.
▢ Adattano la risposta del servizio web agli oggetti dati Kotlin.
▢ Trasformano una chiamata Retrofit in una chiamata al servizio web.
▢ Aggiungono la possibilità di restituire un valore diverso dalla classe Call predefinita in Retrofit.
Inizia la lezione successiva: 
Per i link ad altri codelab di questo corso, consulta la pagina di destinazione dei codelab di Android Kotlin Fundamentals.