Este codelab es parte del curso Conceptos básicos de Kotlin para Android. Aprovecharás al máximo este curso si trabajas con los codelabs de forma secuencial. Todos los codelabs del curso se enumeran en la página de destino de los codelabs de Android Kotlin Fundamentals.
Introducción
Casi cualquier app para Android que compiles deberá conectarse a Internet en algún momento. En este codelab y en los que siguen, compilarás una app que se conecta a un servicio web para recuperar y mostrar datos.  También te basarás en lo que aprendiste en codelabs anteriores sobre ViewModel, LiveData y RecyclerView. 
En este codelab, usarás bibliotecas desarrolladas por la comunidad para compilar la capa de red. Esto simplifica en gran medida la recuperación de datos e imágenes, y también ayuda a la app a cumplir con algunas prácticas recomendadas de Android, como cargar imágenes en un subproceso en segundo plano y almacenar en caché las imágenes cargadas. Para las secciones asíncronas o que no bloquean el código, como la comunicación con la capa de servicios web, modificarás la app para que use las corrutinas de Kotlin. También actualizarás la interfaz de usuario de la app si la conexión a Internet es lenta o no está disponible para que el usuario sepa qué sucede.
Conocimientos que ya deberías tener
- Cómo crear y usar fragmentos
 - Cómo navegar entre fragmentos y usar 
safeArgspara pasar datos entre fragmentos - Cómo usar los componentes de arquitectura, incluidas las transformaciones de 
ViewModel,ViewModelProvider.Factory,LiveDatayLiveData - Cómo usar corrutinas para tareas de larga duración
 
Qué aprenderás
- Qué es un servicio web de REST
 - Cómo usar la biblioteca Retrofit para conectarte a un servicio web REST en Internet y obtener una respuesta
 - Cómo usar la biblioteca Moshi para analizar la respuesta JSON en un objeto de datos
 
Actividades
- Modificarás una app de inicio para realizar una solicitud a la API de servicio web y manejar la respuesta.
 - Implementarás una capa de red para tu app usando la biblioteca Retrofit.
 - Analizarás la respuesta JSON del servicio web en los datos dinámicos de tu app con la biblioteca Moshi.
 - Usarás la compatibilidad de Retrofit para las corrutinas a fin de simplificar el código.
 
En este codelab (y en los siguientes), trabajarás con una app de inicio llamada MarsRealEstate, que muestra propiedades a la venta en Marte. Esta app se conecta a un servicio web para recuperar y mostrar los datos de la propiedad, incluidos detalles como el precio y si la propiedad está disponible para la venta o el alquiler. Las imágenes que representan cada propiedad son fotografías reales de Marte capturadas por los rovers marcianos de la NASA.

La versión de la app que compilas en este codelab no tendrá muchos elementos visuales llamativos, ya que se centra en la parte de la capa de red de la app para conectarse a Internet y descargar los datos de propiedad sin procesar con un servicio web. Para garantizar que los datos se recuperen y analicen correctamente, solo mostrarás la cantidad de propiedades en Marte en una vista de texto:

.
La arquitectura de la app de MarsRealEstate tiene dos módulos principales:
- Un fragmento de descripción general, que contiene una cuadrícula de imágenes de miniaturas de propiedades, compilada con un 
RecyclerView. - Es un fragmento de vista de detalles que contiene información sobre cada propiedad.
 

La app tiene un ViewModel para cada fragmento. En este codelab, crearás una capa para el servicio de red, y ViewModel se comunicará directamente con esa capa de red.  Esto es similar a lo que hiciste en codelabs anteriores cuando el ViewModel se comunicaba con la base de datos Room.
La vista general ViewModel es responsable de realizar la llamada de red para obtener la información de bienes raíces de Marte. El detalle ViewModel contiene detalles de la única propiedad inmobiliaria de Marte que se muestra en el fragmento de detalles.  Para cada ViewModel, usas LiveData con vinculación de datos optimizada para ciclos de vida a fin de actualizar la IU de la app cuando cambian los datos.  
Usas el componente Navigation para navegar entre los dos fragmentos y para pasar la propiedad seleccionada como un argumento.
En esta tarea, descargarás y ejecutarás la app de inicio de MarsRealEstate, y te familiarizarás con la estructura del proyecto.
Paso 1: Explora los fragmentos y la navegación
- Descarga la app de inicio de MarsRealEstate y ábrela en Android Studio.
 - Examina 
app/java/MainActivity.kt. La app usa fragmentos para ambas pantallas, por lo que la única tarea de la actividad es cargar el diseño de la actividad. - Examina 
app/res/layout/activity_main.xml. El diseño de la actividad es el host de los dos fragmentos, definidos en el archivo de navegación. Este diseño crea una instancia deNavHostFragmenty su controlador de navegación asociado con el recursonav_graph. - Abre 
app/res/navigation/nav_graph.xml. Aquí puedes ver la relación de navegación entre los dos fragmentos. El gráfico de navegaciónStartDestinationapunta aoverviewFragment, por lo que se crea una instancia del fragmento de descripción general cuando se inicia la app. 
Paso 2: Explora los archivos fuente de Kotlins y la vinculación de datos
- En el panel Project, expande app > java. Observa que la app de MarsRealEstate tiene tres carpetas de paquetes:  
detail,networkyoverview. Estos corresponden a los tres componentes principales de tu app: los fragmentos de descripción general y de detalles, y el código de la capa de red.
   - Abre 
app/java/overview/OverviewFragment.kt.OverviewFragmentinicializa de forma diferida elOverviewViewModel, lo que significa que elOverviewViewModelse crea la primera vez que se usa. - Revisa el método 
onCreateView(). Este método aumenta el diseño defragment_overviewcon la vinculación de datos, establece el propietario del ciclo de vida para la vinculación (this) y establece la variableviewModelen el objetobinding. Como configuramos el propietario del ciclo de vida, se buscarán cambios automáticamente en cualquierLiveDatautilizado en la vinculación de datos, y la IU se actualizará según corresponda. - Abre 
app/java/overview/OverviewViewModel. Como la respuesta es unLiveDatay establecimos el ciclo de vida para la variable de vinculación, cualquier cambio en ella actualizará la IU de la app. - Examina el bloque 
init. Cuando se crea elViewModel, se llama al métodogetMarsRealEstateProperties(). - Revisa el método 
getMarsRealEstateProperties(). En esta app de inicio, este método contiene una respuesta de marcador de posición. El objetivo de este codelab es que actualices la respuestaLiveDatadentro deViewModelcon datos reales que obtuviste de Internet. - Abre 
app/res/layout/fragment_overview.xml. Este es el diseño del fragmento de descripción general con el que trabajarás en este codelab, y que incluye la vinculación de datos para el ViewModel. ImportaOverviewViewModely, luego, vincula la respuesta deViewModela unTextView. En codelabs posteriores, reemplazarás la vista de texto por una cuadrícula de imágenes en unRecyclerView. - Compila y ejecuta la app. En la versión actual de esta app, solo verás la respuesta inicial: "Set the Mars API Response here!".
 
 
Los datos de bienes raíces de Marte se almacenan en un servidor web como un servicio web de REST. Los servicios web que usan la arquitectura REST se crean con componentes y protocolos web estándar.
Realizas una solicitud a un servicio web de manera estandarizada a través de URIs. La URL web conocida es en realidad un tipo de URI, y ambos se usan de forma intercambiable en este curso. Por ejemplo, en la app de esta lección, recuperas todos los datos del siguiente servidor:
https://android-kotlin-fun-mars-server.appspot.com
Si escribes la siguiente URL en tu navegador, obtendrás una lista de todas las propiedades de bienes raíces disponibles en Marte.
https://android-kotlin-fun-mars-server.appspot.com/realestate
La respuesta de un servicio web suele tener el formato JSON, un formato de intercambio para representar datos estructurados. Obtendrás más información sobre JSON en la siguiente tarea, pero la explicación breve es que un objeto JSON es una colección de pares clave-valor, a veces llamado diccionario, mapa de hash o matriz asociativa. Una colección de objetos JSON es un array JSON, y es el array que obtienes como respuesta de un servicio web.
Para obtener estos datos en la app, esta debe establecer una conexión de red y comunicarse con ese servidor, y, luego, recibir y analizar los datos de respuesta en un formato que la app pueda usar. En este codelab, usarás una biblioteca de cliente de REST llamada Retrofit para realizar esta conexión.
Paso 1: Agrega dependencias de Retrofit a Gradle
- Abre build.gradle (Module: app).
 - En la sección 
dependencies, agrega estas líneas para las bibliotecas Retrofit: 
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
Observa que los números de versión se definen por separado en el archivo Gradle del proyecto. La primera dependencia es para la biblioteca Retrofit 2 y la segunda es para el conversor escalar de Retrofit.  Este conversor permite que Retrofit muestre el resultado JSON como String. Las dos bibliotecas funcionan juntas.
- Haz clic en Sync Now para volver a compilar el proyecto con las dependencias nuevas.
 
Paso 2: Implementa MarsApiService
Retrofit crea una API de red para la app basada en el contenido del servicio web. Recupera datos del servicio web y los enruta a través de una biblioteca de conversor independiente que sabe cómo decodificar los datos y mostrarlos en forma de objetos útiles. Retrofit incluye compatibilidad integrada para formatos de datos web populares, como XML y JSON. En última instancia, Retrofit crea la mayor parte de la capa de red por ti, incluidos los detalles críticos, como la ejecución de solicitudes en subprocesos en segundo plano.
La clase MarsApiService contiene la capa de red de la app, es decir, esta es la API que tu ViewModel usará para comunicarse con el servicio web.  Esta es la clase en la que implementarás la API del servicio de Retrofit.  
- Abre 
app/java/network/MarsApiService.kt. Por el momento, el archivo contiene solo una cosa: una constante para la URL base del servicio web. 
private const val BASE_URL = 
   "https://android-kotlin-fun-mars-server.appspot.com"- Justo debajo de esa constante, usa un compilador de Retrofit para crear un objeto Retrofit.  Importa 
retrofit2.Retrofityretrofit2.converter.scalars.ScalarsConverterFactorycuando se te solicite. 
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()
Retrofit necesita al menos dos elementos disponibles para compilar una API de servicios web: el URI base para el servicio web y una fábrica de conversión.  El conversor le indica a Retrofit qué hacer con los datos que obtiene del servicio web.  En este caso, Retrofit tendría que recuperar una respuesta JSON del servicio web y mostrarla como String.  Retrofit tiene un ScalarsConverter que admite strings y otros tipos primitivos, por lo que llamas a addConverterFactory() en el compilador con una instancia de ScalarsConverterFactory.  Por último, llamas a build() para crear el objeto Retrofit.
- Justo debajo de la llamada al compilador de Retrofit, define una interfaz que defina cómo Retrofit se comunica con el servidor web mediante solicitudes HTTP.  Importa 
retrofit2.http.GETyretrofit2.Callcuando se te solicite. 
interface MarsApiService {
    @GET("realestate")
    fun getProperties():
            Call<String>
}Por el momento, el objetivo es obtener la cadena de respuesta JSON del servicio web, y solo necesitas un método para hacerlo: getProperties().  Para indicarle a Retrofit lo que debe hacer este método, usa una anotación @GET y especifica la ruta de acceso o el extremo para ese método de servicio web.  En este caso, el extremo se llama realestate. Cuando se invoca el método getProperties(), Retrofit agrega el extremo realestate a la URL base (que definiste en el compilador de Retrofit) y crea un objeto Call. Ese objeto Call se usa para iniciar la solicitud.
- Debajo de la interfaz 
MarsApiService, define un objeto público llamadoMarsApipara inicializar el servicio de Retrofit. 
object MarsApi {
    val retrofitService : MarsApiService by lazy { 
       retrofit.create(MarsApiService::class.java) }
}El método create() de Retrofit crea el servicio de Retrofit con la interfaz MarsApiService.  Dado que esta llamada es costosa y la app solo necesita una instancia del servicio de Retrofit, expones el servicio al resto de la app con un objeto público llamado MarsApi y, luego, inicializas el servicio de Retrofit de forma diferida. Ahora que se completó toda la configuración, cada vez que tu app llame a MarsApi.retrofitService,  obtendrá un objeto singleton de Retrofit que implementa MarsApiService.
Paso 3: Llama al servicio web en OverviewViewModel
- Abre 
app/java/overview/OverviewViewModel.kt. Desplázate hacia abajo hasta el métodogetMarsRealEstateProperties(). 
private fun getMarsRealEstateProperties() {
   _response.value = "Set the Mars API Response here!"
}Este es el método en el que llamarás al servicio de Retrofit y controlarás la cadena JSON que se muestra. Por el momento, solo hay una cadena de texto de marcador de posición para la respuesta.
- Borra la línea del marcador de posición que establece la respuesta en "Set the Mars API Response here!".
 - Dentro de 
getMarsRealEstateProperties(), agrega el código que se muestra a continuación. Importaretrofit2.Callbackycom.example.android.marsrealestate.network.MarsApicuando se te solicite.
El métodoMarsApi.retrofitService.getProperties()devuelve un objetoCall. Luego, puedes llamar aenqueue()en ese objeto para iniciar la solicitud de red en un subproceso en segundo plano. 
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<String> {
})- Haz clic en la palabra 
object, que está subrayada en rojo. Selecciona Code > Implement methods. SeleccionaonResponse()yonFailure()en la lista.
Android Studio agrega el código con TODOs en cada método: 
override fun onFailure(call: Call<String>, t: Throwable) {
       TODO("not implemented") 
}
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
       TODO("not implemented") 
}- En 
onFailure(), borra el comentario TODO y establece el_responseen un mensaje de error, como se muestra a continuación. El_responsees una cadenaLiveDataque determina lo que se muestra en la vista de texto. Cada estado debe actualizar el_responseLiveData.
Se llama a la devolución de llamadaonFailure()cuando falla la respuesta del servicio web. Para esta respuesta, establece el estado_responseen"Failure: "concatenado con el mensaje del argumentoThrowable. 
override fun onFailure(call: Call<String>, t: Throwable) {
   _response.value = "Failure: " + t.message
}- En 
onResponse(), borra el comentario TODO y establece_responseen el cuerpo de la respuesta. Se llama a la devolución de llamadaonResponse()cuando la solicitud se realiza correctamente y el servicio web devuelve una respuesta. 
override fun onResponse(call: Call<String>, 
   response: Response<String>) {
      _response.value = response.body()
}Paso 4: Define el permiso de Internet
- Compila y ejecuta la app de MarsRealEstate.  Ten en cuenta que la app se cierra de inmediato con un error. 
   - En Android Studio, haz clic en la pestaña Logcat y observa el error en el registro, que comienza con una línea como la siguiente:
 
Process: com.example.android.marsrealestate, PID: 10646 java.lang.SecurityException: Permission denied (missing INTERNET permission?)
El mensaje de error indica que es posible que a la app le falte el permiso INTERNET. La conexión a Internet presenta problemas de seguridad, por lo que las apps no tienen conectividad a Internet de forma predeterminada. Debes indicarle explícitamente a Android que la app necesita acceso a Internet.
- Abre 
app/manifests/AndroidManifest.xml. Agrega esta línea justo antes de la etiqueta<application>: 
<uses-permission android:name="android.permission.INTERNET" />- Vuelve a compilar y ejecutar la app.  Si todo funciona correctamente con tu conexión a Internet, verás el texto JSON que contiene datos de la propiedad de Marte.

 - Presiona el botón Atrás en el dispositivo o emulador para cerrar la app.
 - Pon tu dispositivo o emulador en modo de avión y, luego, vuelve a abrir la app desde el menú Recientes o reinicia la app desde Android Studio.
 

- Vuelve a desactivar el modo de avión.
 
Ahora obtienes una respuesta JSON del servicio web de Marte, que es un buen comienzo. Pero lo que necesitas son objetos Kotlin, no una string JSON grande. Hay una biblioteca llamada Moshi, que es un analizador de JSON de Android que convierte una cadena JSON en objetos Kotlin. Retrofit tiene un conversor que funciona con Moshi, por lo que es una excelente biblioteca para sus propósitos aquí.
En esta tarea, usarás la biblioteca Moshi con Retrofit para analizar la respuesta JSON del servicio web en objetos de Kotlin útiles de Mars Property. Cambiarás la app de modo que en lugar de mostrar el JSON sin procesar, muestre la cantidad de propiedades de Marte que se muestran.
Paso 1: Agrega dependencias de la biblioteca Moshi
- Abre build.gradle (Module: app).
 - En la sección de dependencias, agrega el código que se muestra a continuación para incluir las dependencias de Moshi. Al igual que con Retrofit, 
$version_moshise define por separado en el archivo de Gradle a nivel del proyecto. Estas dependencias agregan compatibilidad con la biblioteca JSON principal de Moshi y con la compatibilidad de Moshi con Kotlin. 
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"- Busca la línea del conversor escalar de Retrofit en el bloque 
dependencies: 
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"- Cambia esa línea para usar 
converter-moshi: 
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"- Haz clic en Sync Now para volver a compilar el proyecto con las dependencias nuevas.
 
Paso 2: Implementa la clase de datos de MarsProperty
Una entrada de muestra de la respuesta JSON que obtienes del servicio web se ve de la siguiente manera:
[{"price":450000,
"id":"424906",
"type":"rent",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"},
...]La respuesta JSON que se muestra arriba es un array, que se indica con los corchetes.  El array contiene objetos JSON, que están entre llaves.  Cada objeto contiene un conjunto de pares nombre-valor, separados por dos puntos. Los nombres aparecen entre comillas. Los valores pueden ser números o cadenas, y las cadenas también están entre comillas. Por ejemplo, el price de esta propiedad es USD 450,000 y el img_src es una URL, que es la ubicación del archivo de imagen en el servidor. 
En el ejemplo anterior, observa que cada entrada de propiedad de Marte tiene estos pares clave-valor y valores JSON:
price: Es el precio de la propiedad de Mars, como un número.id: El ID de la propiedad, como una string.type: por"rent"o"buy".img_src: La URL de la imagen como una string.
Moshi analiza estos datos JSON y los convierte en objetos Kotlin. Para ello, necesita tener una clase de datos de Kotlin para almacenar los resultados analizados, por lo que el siguiente paso es crear esa clase.
- Abre 
app/java/network/MarsProperty.kt. - Reemplaza la definición de clase 
MarsPropertyexistente por el siguiente código: 
data class MarsProperty(
   val id: String, val img_src: String,
   val type: String,
   val price: Double
)Observa que cada una de las variables de la clase MarsProperty corresponde a un nombre de clave en el objeto JSON.  Para hacer coincidir los tipos en el JSON, usas objetos String para todos los valores, excepto price, que es un Double.  Se puede usar un Double para representar cualquier número JSON.
Cuando Moshi analiza el JSON, busca las claves por nombre y completa los objetos de datos con los valores correspondientes.
- Reemplaza la línea para la clave 
img_srccon la línea que se muestra a continuación. Importacom.squareup.moshi.Jsoncuando se te solicite. 
@Json(name = "img_src") val imgSrcUrl: String,A veces, los nombres de clave en una respuesta JSON pueden hacer que las propiedades de Kotlin sean confusas o no coincidan con tu estilo de codificación. Por ejemplo, en el archivo JSON, la clave img_src usa un guion bajo, mientras que las propiedades de Kotlin suelen usar letras mayúsculas y minúsculas ("mayúsculas mediales"). 
Para usar nombres de variables en tu clase de datos que difieren de los nombres de clave en la respuesta JSON, usa la anotación @Json. En este ejemplo, el nombre de la variable en la clase de datos es imgSrcUrl. La variable se asigna al atributo JSON img_src usando @Json(name = "img_src").
Paso 3: Actualiza MarsApiService y OverviewViewModel
Con la clase de datos MarsProperty implementada, ahora puedes actualizar la API de la red y ViewModel para incluir los datos de Moshi.  
- Abre 
network/MarsApiService.kt. Es posible que veas errores de clase faltante paraScalarsConverterFactory. Esto se debe al cambio de dependencia de Retrofit que realizaste en el paso 1. Pronto corregirás esos errores. - En la parte superior del archivo, justo antes de la creación de Retrofit, agrega el siguiente código para crear la instancia de Moshi.  Importa 
com.squareup.moshi.Moshiycom.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactorycuando se te solicite. 
private val moshi = Moshi.Builder()
   .add(KotlinJsonAdapterFactory())
   .build()De manera similar a lo que hiciste con Retrofit, aquí crearás un objeto moshi con el compilador de Moshi.  Para que las anotaciones de Moshi funcionen correctamente con Kotlin, agrega KotlinJsonAdapterFactory y, luego, llama a build().  
- Cambia el compilador de Retrofit para usar 
MoshiConverterFactoryen lugar deScalarConverterFactoryy pasa la instancia demoshique acabas de crear. Importaretrofit2.converter.moshi.MoshiConverterFactorycuando se te solicite. 
private val retrofit = Retrofit.Builder()
   .addConverterFactory(MoshiConverterFactory.create(moshi))
   .baseUrl(BASE_URL)
   .build()- Borra también la importación de 
ScalarConverterFactory. 
Código que debes borrar:
import retrofit2.converter.scalars.ScalarsConverterFactory- Actualiza la interfaz 
MarsApiServicepara que Retrofit muestre una lista de objetosMarsProperty, en lugar de mostrarCall<String>. 
interface MarsApiService {
   @GET("realestate")
   fun getProperties():
      Call<List<MarsProperty>>
}- Abre 
OverviewViewModel.kt. Desplázate hacia abajo hasta la llamada agetProperties().enqueue()en el métodogetMarsRealEstateProperties(). - Cambia el argumento a 
enqueue()deCallback<String>aCallback<List<MarsProperty>>. Importacom.example.android.marsrealestate.network.MarsPropertycuando se solicite. 
MarsApi.retrofitService.getProperties().enqueue( 
   object: Callback<List<MarsProperty>> {- En 
onFailure(), cambia el argumento deCall<String>aCall<List<MarsProperty>>: 
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {- Realiza el mismo cambio en ambos argumentos de 
onResponse(): 
override fun onResponse(call: Call<List<MarsProperty>>, 
   response: Response<List<MarsProperty>>) {- En el cuerpo de 
onResponse(), reemplaza la asignación existente a_response.valuepor la asignación que se muestra a continuación. Comoresponse.body()ahora es una lista de objetosMarsProperty, el tamaño de esa lista es la cantidad de propiedades que se analizaron. Este mensaje de respuesta imprime esa cantidad de propiedades: 
_response.value = 
   "Success: ${response.body()?.size} Mars properties retrieved"- Asegúrate de que el modo de avión esté desactivado.  Compila y ejecuta la app. Esta vez, el mensaje debería mostrar la cantidad de propiedades que se muestran del servicio web:

 
Ahora se está ejecutando el servicio de la API de Retrofit, pero usa una devolución de llamada con dos métodos de devolución de llamada que tuviste que implementar. Un método controla el éxito y otro controla el error, y el resultado del error informa las excepciones. Tu código sería más eficiente y fácil de leer si pudieras usar corrutinas con el control de excepciones, en lugar de usar devoluciones de llamada. Convenientemente, Retrofit tiene una biblioteca que integra corrutinas.
En esta tarea, convertirás tu servicio de red y el ViewModel para que usen corrutinas.  
Paso 1: Agrega dependencias de corrutinas
- Abre build.gradle (Module: app).
 - En la sección de dependencias, agrega compatibilidad con las bibliotecas principales de corrutinas de Kotlin y la biblioteca de corrutinas de 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"
- Haz clic en Sync Now para volver a compilar el proyecto con las dependencias nuevas.
 
Paso 2: Actualiza MarsApiService y OverviewViewModel
- En 
MarsApiService.kt, actualiza el compilador de Retrofit para usar elCoroutineCallAdapterFactory. El compilador completo ahora se ve de la siguiente manera: 
private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .baseUrl(BASE_URL)
        .build()Los adaptadores de llamadas agregan la capacidad de Retrofit para crear APIs que devuelven algo distinto de la clase Call predeterminada.  En este caso, CoroutineCallAdapterFactory nos permite reemplazar el objeto Call que devuelve getProperties() por un objeto Deferred.
- En el método 
getProperties(), cambiaCall<List<MarsProperty>>aDeferred<List<MarsProperty>>. Importakotlinx.coroutines.Deferredcuando se solicite. El métodogetProperties()completo se ve de la siguiente manera: 
@GET("realestate")
fun getProperties():
   Deferred<List<MarsProperty>>La interfaz Deferred define un trabajo de corrutina que devuelve un valor de resultado (Deferred hereda de Job). La interfaz Deferred incluye un método llamado await(), que hace que tu código espere sin bloquearse hasta que el valor esté listo y, luego, se devuelva ese valor. 
- Abre 
OverviewViewModel.kt. Justo antes del bloqueinit, agrega un trabajo de corrutina: 
private var viewModelJob = Job()- Crea un alcance de corrutina para ese trabajo nuevo con el despachador principal:
 
private val coroutineScope = CoroutineScope(
   viewModelJob + Dispatchers.Main )El despachador Dispatchers.Main usa el subproceso de IU para su trabajo.  Como Retrofit realiza todo su trabajo en un subproceso en segundo plano, no hay motivos para usar ningún otro subproceso para el alcance. Esto te permite actualizar fácilmente el valor de MutableLiveData cuando obtienes un resultado.
- Borra todo el código dentro de 
getMarsRealEstateProperties(). Aquí usarás corrutinas en lugar de la llamada aenqueue()y las devoluciones de llamadasonFailure()yonResponse(). - Dentro de 
getMarsRealEstateProperties(), inicia la corrutina: 
coroutineScope.launch { 
}
Para usar el objeto Deferred que Retrofit devuelve para la tarea de red, debes estar dentro de una corrutina, por lo que aquí inicias la corrutina que acabas de crear.  Aún ejecutas código en el subproceso principal, pero ahora permites que las corrutinas administren la simultaneidad.
- Dentro del bloque de inicio, llama a 
getProperties()en el objetoretrofitService: 
var getPropertiesDeferred = MarsApi.retrofitService.getProperties()Llamar a getProperties() desde el servicio MarsApi crea e inicia la llamada de red en un subproceso en segundo plano, y devuelve el objeto Deferred para esa tarea. 
- También dentro del bloque de lanzamiento, agrega un bloque 
try/catchpara controlar las excepciones: 
try {
} catch (e: Exception) {
  
}- Dentro del bloque 
try {}, llama aawait()en el objetoDeferred: 
var listResult = getPropertiesDeferred.await()Si llamas a await() en el objeto Deferred, se muestra el resultado de la llamada de red cuando el valor está listo. El método await() no es de bloqueo, por lo que el servicio de la API de Mars recupera los datos de la red sin bloquear el subproceso actual, lo que es importante porque estamos en el alcance del subproceso de IU. Una vez que se completa la tarea, tu código continúa ejecutándose desde donde se detuvo.  Esto se encuentra dentro de try {} para que puedas detectar excepciones.  
- También dentro del bloque 
try {}, después del métodoawait(), actualiza el mensaje de respuesta para la respuesta exitosa: 
_response.value = 
   "Success: ${listResult.size} Mars properties retrieved"- Dentro del bloque 
catch {}, controla la respuesta de falla: 
_response.value = "Failure: ${e.message}"
El método getMarsRealEstateProperties() completo ahora tiene el siguiente aspecto:
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}"
       }
   }
}- En la parte inferior de la clase, agrega una devolución de llamada 
onCleared()con este código: 
override fun onCleared() {
   super.onCleared()
   viewModelJob.cancel()
}La carga de datos debe detenerse cuando se destruye el ViewModel, ya que el OverviewFragment que usa este ViewModel desaparecerá. Para detener la carga cuando se destruye el ViewModel, anula onCleared() para cancelar el trabajo.
- Compila y ejecuta la app. Esta vez, obtendrás el mismo resultado que en la tarea anterior (un informe de la cantidad de propiedades), pero con un código y un control de errores más sencillos.
 
Proyecto de Android Studio: MarsRealEstateNetwork
Servicios web de REST
- Un servicio web es un servicio en Internet que permite que tu app haga solicitudes y recupere datos.
 - Los servicios web comunes usan una arquitectura REST. Los servicios web que ofrecen arquitectura REST se conocen como servicios RESTful. Los servicios web RESTful se crean con componentes y protocolos web estándar.
 - Realizas una solicitud a un servicio web REST de manera estandarizada mediante URI.
 - Para usar un servicio web, una app debe establecer una conexión de red y comunicarse con el servicio. Luego, la app debe recibir y analizar los datos de respuesta a un formato que pueda usar la app.
 - La biblioteca Retrofit es una biblioteca cliente que permite a tu app realizar solicitudes a un servicio web REST.
 - Usar conversores para indicarle a Retrofit qué hacer con los datos que envía al servicio web y regresa del servicio web.  Por ejemplo, el conversor de 
ScalarsConvertertrata los datos del servicio web comoStringo cualquier otra primitiva. - Para permitir que tu app establezca conexiones a Internet, agrega el permiso 
"android.permission.INTERNET"en el manifiesto de Android. 
Análisis de JSON
- A menudo, la respuesta de un servicio web tiene el formato JSON, un formato de intercambio común para representar datos estructurados.
 - Un objeto JSON es una colección de pares clave-valor. A veces, esta colección se denomina diccionario, mapa de hash o array asociativo.
 - Una colección de objetos JSON es un array JSON. Obtienes un array JSON como una respuesta de un servicio web.
 - Las claves en un par clave-valor están entre comillas. Los valores pueden ser números o strings. Las cadenas también se escriben entre comillas.
 - La biblioteca Moshi es un analizador de JSON de Android que convierte una cadena JSON en objetos Kotlin. Retrofit tiene un conversor que funciona con Moshi.
 - Moshi hace coincidir las claves en una respuesta JSON con propiedades en un objeto de datos que tienen el mismo nombre.
 - A fin de usar un nombre de propiedad diferente para una clave, anota esa propiedad con la anotación 
@Jsony el nombre de la clave JSON. 
Retrofit y corrutinas
- Los adaptadores de llamadas permiten que Retrofit cree APIs que devuelvan algo distinto de la clase 
Callpredeterminada. Usa la claseCoroutineCallAdapterFactorypara reemplazar elCallpor una corrutinaDeferred. - Usa el método 
await()en el objetoDeferredpara que el código de la corrutina espere sin bloquearse hasta que el valor esté listo y, luego, se muestre el valor. 
Curso de Udacity:
Documentación para desarrolladores de Android:
Documentación de Kotlin:
Otro:
En esta sección, se enumeran las posibles actividades para el hogar para los alumnos que trabajan en este codelab como parte de un curso dirigido por un instructor. Depende del instructor hacer lo siguiente:
- Si es necesario, asigna una tarea.
 - Comunicarles a los alumnos cómo enviar las actividades para el hogar.
 - Califica las actividades para el hogar.
 
Los instructores pueden usar estas sugerencias en la medida que quieran y deben asignar cualquier otra actividad para el hogar que consideren apropiada.
Si estás trabajando en este codelab por tu cuenta, usa estas actividades para el hogar para probar tus conocimientos.
Responde estas preguntas:
Pregunta 1
¿Cuáles son los dos elementos clave que necesita Retrofit para compilar una API de servicios web?
▢ El URI base para el servicio web y una consulta GET
▢ El URI base para el servicio web y una fábrica de convertidor.
▢ Una conexión de red al servicio web y un token de autorización.
▢ Una fábrica de convertidor y un analizador para la respuesta.
Pregunta 2
¿Cuál es el propósito de la biblioteca Moshi?
▢ Recuperar datos de un servicio web
▢ Interactuar con Retrofit para realizar una solicitud de servicio web
▢ Analizar una respuesta JSON de un servicio web en objetos de datos de Kotlin
▢ Cambiar el nombre de los objetos de Kotlin para que coincidan con las claves en la respuesta JSON
Pregunta 3
¿Para qué se usan los adaptadores de llamada de Retrofit?
▢ Habilitan Retrofit para usar corrutinas.
▢ Adaptan la respuesta del servicio web a objetos de datos de Kotlin.
▢ Cambian una llamada de Retrofit por una llamada de servicio web.
▢ Agregan la capacidad de devolver algo que no sea la clase Call predeterminada en Retrofit.
Comienza la siguiente lección: 
Para obtener vínculos a otros codelabs de este curso, consulta la página de destino de los codelabs de Conceptos básicos de Kotlin para Android.