Enviando Datos Con el Cliente HTTP Retrofit 2 para Android
Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)



¿Qué Es Retrofit?
Retrofit es un cliente HTTP de tipo seguro para Android y Java. Retrofit hace sencillo conectar a un servicio web REST traduciendo la API a interfaces Java. En este tutorial, te mostraré cómo usar una de las librerías HTTP más populares y frecuentemente recomendada disponible para Android.
Esta poderosa librería hace sencillo consumir datos JSON o XML, que después es analizado en Objetos Java (Plain Old Java Objects, POJOs). Todas las peticiones GET
, POST
, PUT
, PATCH
, and DELETE
pueden ser ejecutadas.
Como la mayoría del software de código abierto, Retrofit fue construido encima de algunas otras librerías y herramientas poderosas. Tras bambalinas, Retrofit hace uso de OkHttp (del mismo desarrollador) para manejar peticiones de red. También, Retrofit no tiene un convertidor JSON integrado para convertir de objetos JSON a Java. En su lugar, viene con soporte para las siguientes librerías de convertidor JSON para manejar eso:
- Gson:
com.squareup.retrofit:converter-gson
- Jackson:
com.squareup.retrofit:converter-jackson
- Moshi:
com.squareup.retrofit:converter-moshi
Para buffers de Protocolo, Retrofit soporta:
- Protobuf:
com.squareup.retrofit2:converter-protobuf
- Wire:
com.squareup.retrofit2:converter-wire
Y para XML Retrofit soporta:
- Simple Framework:
com.squareup.retrofit2:converter-simpleframework
¿Así Qué Por Qué Usar Retrofit?
Desarrollar tu propia librería HTTP de tipo seguro para hacer interfaz con una API REST puede ser bastante tedioso: tienes que manejar muchos aspectos, tales como hacer conexiones, cachear, reintentar peticiones fallidas, hilación, análisis de respuesta, manejo de errores, y más. Retrofit, por otro lado, es una librería bien planeada, documentada y probada que te ahorrará mucho tiempo valioso y dolores de cabeza.
En este tutorial explicaré cómo usar Retrofit 2 para manejar peticiones de red construyendo una aplicación simple que realizará peticiones POST
, peticiones PUT
(para actualizar entidades), y peticiones DELETE
. También te mostraré cómo integrar con RxJava y cómo cancelar peticiones. Estaremos usando la API provista por JSONPlaceholder---esta es una API REST falsa en línea para pruebas y prototipado.
Revisa mi tutorial anterior, Comenzando Con el Cliente HTTP Retrofit 2, para aprender cómo ejecutar peticiones GET
y cómo integrar Retrofit con RxJava.
1. Crea un Proyecto Android Studio
Inicia Android Studio y crea un nuevo proyecto con una actividad vacía llamada MainActivity
.
2. Declarando Dependencias
Después de crear un nuevo proyecto, declara las siguientes dependencias en tu build.gradle
. Las dependencias incluyen la librería Retrofit y también la librería Gson de Google para convertir JSON a POJO (Plan Old Java Objects) así como la integración Gson de Retrofit.
// Retrofit compile 'com.squareup.retrofit2:retrofit:2.1.0' // JSON Parsing compile 'com.google.code.gson:gson:2.6.1' compile 'com.squareup.retrofit2:converter-gson:2.1.0'
Asegúrate de sincronizar tu proyecto después de agregar las dependencias.
3. Agregando el Permiso de Internet
Para realizar operaciones de red, necesitamos incluir el permiso INTERNET
en el manifest de la aplicación: AndroidManifest.xml.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.chikeandroid.retrofittutorial2"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".PostActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
4. Generando Modelos Automáticamente
Vamos a crear modelos automáticamente desde la información de respuesta JSON aprovechando una herramienta bastante útil: jsonschema2pojo. Nos gustaría hacer una petición POST
(crear un nuevo recurso) sobre la API. Pero antes de ejecutar esta petición, necesitamos saber la respuesta JSON que deberíamos esperar cuando se ejecuta exitosamente para que Retrofit pueda analizar la respuesta JSON y deserializarla a objetos Java. De acuerdo a la API, si enviamos los siguientes datos en una petición POST
:
data: { title: 'foo', body: 'bar', userId: 1 }
Deberíamos obtener la siguiente respuesta:
{ "title": "foo", "body": "bar", "userId": 1, "id": 101 }
Mapea los Datos JSON a Java
Copia los datos de respuesta de muestra de la sección anterior. Ahora visita jsonschema2pojo y pega la respuesta JSON en la caja de entrada. Selecciona el tipo de fuente de JSON, estilo de anotación de Gson, y desmarca Permitir propiedades adicionales, y cambia el nombre de la clase de Ejemplo a Post.



Después da clic al botón Vista Previa para generar los objetos Java.



¡Podrías estarte preguntando lo que hacen las anotaciones @SerializedName
y @Expose
en este código generado! ¡No te preocupes, lo explicaré todo!
La anotación @SerializedName
es necesaria para que Gson mapee las llaves JSON a campos de objeto Java.
@SerializedName("userId") @Expose private Integer userId;
En este caso, la llave JSON userId
es mapeada al campo de clase userId
. Pero nota que ya que son el mismo, no hay necesidad de incluir la anotación @SerializedName
en el campo porque Gson los mapeará automáticamente por nosotros.
La anotación @Expose
indica que el miembro de la clase debería ser expuesto para serialización o deserialización JSON.
Importa Modelos de Datos a Android Studio
Ahora regresemos a Android Studio. Crea un nuevo sub-paquete dentro del paquete main
y llámalo data
. Dentro del recién creado paquete, crea otro paquete y llámalo model
. Dentro de este paquete, crea una nueva clase Java y llámala Post
. Ahora copia la clase Post
que fue generada por jsonschema2pojo y pégala dentro de la clase Post
que creaste.
package com.chikeandroid.retrofittutorial2.data.model; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; public class Post { @SerializedName("title") @Expose private String title; @SerializedName("body") @Expose private String body; @SerializedName("userId") @Expose private Integer userId; @SerializedName("id") @Expose private Integer id; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Override public String toString() { return "Post{" + "title='" + title + '\'' + ", body='" + body + '\'' + ", userId=" + userId + ", id=" + id + '}'; } }
Además de los getters y setters, también incluí el método toString()
. (En Intellij, puedes usar el comando Generate para hacer esto fácil: Alt-Insertar en Windows, o Comando-N en macOS.)
5. Creando la Instancia Retrofit
Para emitir peticiones de red a una API RESTFUL con Retrofit, necesitamos crear una instancia usando la clase Retrofit Builder y configurarla con una URL base.
Crea un nuevo sub-paquete dentro del paquete data
y llámalo remote
. Ahora, dentro de este paquete, crea una clase Java y llámala RetrofitClient
. Esta clase creará un semifallo Retrofit en el método getClient(String baseUrl)
y lo devolverá al llamador.
Como mencioné anteriormente, Retrofit necesita una URL base para construir su instancia, así que le pasaremos una URL cuando llamemos a RetrofitClient.getClient(String baseUrl)
. Esta URL será entonces usada para construir la instancia en la línea 13. También estamos especificando el convertidor JSON que necesitamos (Gson) en la línea 14.
package com.chikeandroid.retrofittutorial2.data.remote; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class RetrofitClient { private static Retrofit retrofit = null; public static Retrofit getClient(String baseUrl) { if (retrofit==null) { retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build(); } return retrofit; } }
6. Creando la Interfaz API
Dentro del paquete remoto, crea una interfaz y llámala APIService
. Esta interfaz contiene métodos que vamos a usar para ejecutar peticiones HTTP tales como POST
, PUT
, y DELETE
. Comencemos con la petición POST
.
package com.chikeandroid.retrofittutorial2.data.remote; import com.chikeandroid.retrofittutorial2.data.model.Post; import retrofit2.Call; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.POST; public interface APIService { @POST("/posts") @FormUrlEncoded Call<Post> savePost(@Field("title") String title, @Field("body") String body, @Field("userId") long userId); }
Viendo la clase APIService
, tenemos un método savePost()
. Encima del método está la anotación @POST
, que indica que queremos ejecutar una petición POST
cuando este método es llamado. El valor de argumento para la anotación @POST
es el punto fina---que es /posts
. Así que la URL completa sería http://jsonplaceholder.typicode.com/posts.
De acuerdo, ¿pero qué sobre el @FormUrlEncoded
? Esto indicará que la petición tendrá su tipo MIME (un campo de encabezado que identifica el formato del cuerpo de una petición o respuesta HTTP) establecido a application/x-www-form-urlencoded
y también que sus nombres de campo y valores serán codificados en UTF-8 antes de ser codificados en URI. La anotación @Field("key")
con nombre de parámetro debería empatar el nombre que la API espera. Retrofit convierte implícitamente los valores a cadenas de texto usando String.valueOf(Object)
, y las cadenas son entonces codificadas en forma de URL. Los valores null
son ignorados.
Por ejemplo, llamar a APIService.savePost("My Visit To Lagos", "I visited...", 2)
produce un cuerpo de petición de title=My+Visit+To+Lagos&body=I+visited...&userId=2
Usando la Anotación @Body
También podemos usar la anotación @Body
en un parámetro de método de servicio en lugar de especificar un cuerpo de petición estilo formulario con un número de campos individuales. El objeto será serializado usando la instancia Converter
de Retrofit
especificada durante la creación. Esto solo es usado cuando se realiza una operación POST
o PUT
.
@POST("/posts") @FormUrlEncoded Call<Post> savePost(@Body Post post);
7. Creando la Utilidades API
Vamos a crear una clase de utilidad. Así que crea una clase en data.remote
y llámala ApiUtils
. Esta clase tendrá la URL base como una variable estática y también proporcionará la interfaz APIService
con un método estático getAPIService()
al resto de nuestra aplicación.
package com.chikeandroid.retrofittutorial2.data.remote; public class ApiUtils { private ApiUtils() {} public static final String BASE_URL = "http://jsonplaceholder.typicode.com/"; public static APIService getAPIService() { return RetrofitClient.getClient(BASE_URL).create(APIService.class); } }
Asegúrate de que terminas la URL base con una /
.
8. Creando el Diseño
El archivo activity_main.xml es el diseño para nuestro MainActivity
. Este diseño tendrá un campo text field para el título del post y otro para el cuerpo del post. También incluye un botón para enviar el post a la API.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_post" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context="com.chikeandroid.retrofittutorial2.AddEditPostActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:textAppearance="@style/TextAppearance.AppCompat.Title" android:text="@string/title_enter_post"/> <EditText android:id="@+id/et_title" android:layout_marginTop="18dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hint_title"/> <EditText android:id="@+id/et_body" android:lines="4" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hint_body"/> <Button android:id="@+id/btn_submit" android:layout_marginTop="18dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent" android:textColor="@android:color/white" android:text="@string/action_submit"/> <TextView android:id="@+id/tv_response" android:layout_marginTop="35dp" android:visibility="gone" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
9. Ejecutando la petición POST
En el método onCreate()
en MainActivity
, inicializamos una instancia de la interfaz APIService
(línea 14). También inicializamos los campos EditText
y un botón enviar que llamará al método sendPost()
cuando se le de clic (línea 22).
private TextView mResponseTv; private APIService mAPIService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final EditText titleEt = (EditText) findViewById(R.id.et_title); final EditText bodyEt = (EditText) findViewById(R.id.et_body); Button submitBtn = (Button) findViewById(R.id.btn_submit); mResponseTv = (TextView) findViewById(R.id.tv_response); mAPIService = ApiUtils.getAPIService(); submitBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String title = titleEt.getText().toString().trim(); String body = bodyEt.getText().toString().trim(); if(!TextUtils.isEmpty(title) && !TextUtils.isEmpty(body)) { sendPost(title, body); } } }); }
En el método sendPost(String, String)
en la clase MainActivity
, pasamos el título y el cuerpo del post a este método. Lo que hará este método es llamar a nuestro método de interfaz de servicio API savePost(String, String)
cuyo trabajo es ejecutar una petición POST
enviando el título y el cuerpo a la API. El método showResponse(String response)
mostrará la respuesta en la pantalla.
public void sendPost(String title, String body) { mAPIService.savePost(title, body, 1).enqueue(new Callback<Post>() { @Override public void onResponse(Call<Post> call, Response<Post> response) { if(response.isSuccessful()) { showResponse(response.body().toString()); Log.i(TAG, "post submitted to API." + response.body().toString()); } } @Override public void onFailure(Call<Post> call, Throwable t) { Log.e(TAG, "Unable to submit post to API."); } }); } public void showResponse(String response) { if(mResponseTv.getVisibility() == View.GONE) { mResponseTv.setVisibility(View.VISIBLE); } mResponseTv.setText(response); }
El método savePost(String, String)
de la instancia mAPIService
de nuestro APIService
devolverá una instancia Call
que tiene un método llamado enqueue(Callback callback).
Entendiendo enqueue()
enqueue()
envía de manera asíncrona la petición y notifica a tu aplicación con un callback cuando una respuesta regresa. Ya que esta petición es asíncrona, Retrofit maneja la ejecución en el hilo de fondo para que el hilo de la UI principal no sea bloqueada o interfiera con esta.
Para usar el método enqueue()
, tienes que implementar dos métodos callback: onResponse()
y onFailure()
. Solo uno de estos métodos será llamado en respuesta a una petición dada.
-
onResponse()
: invocado para una respuesta HTTP recibida. Este método es llamado para una respuesta que puede ser correctamente manejada incluso si el servidor devuelve un mensaje de error. Así que si obtienes un código de estado 404 o 500, este método aún será llamado. Para obtener el código de estado para que puedas manejar situaciones basadas en estos, puedes usar el métodoresponse.code()
. También puedes usar el métodoisSuccessful()
para averiguar si el código de estado está en el rango 200-300, indicando éxito. -
onFailure()
: invocado cuando una excepción de red ocurre comunicando al servidor o cuando una excepción inesperada ocurrió manejando la petición o procesando la respuesta.
Peticiones Síncronas
Para realizar una petición síncrona, puedes usar el método execute()
en una instancia Call
. Pero ten en cuenta que los métodos síncronos en el hilo main/UI bloqueará cualquier acción del usuario. ¡Así que no ejecutes métodos síncronos en el hilo main/UI de Android! En su lugar ejecútalos en un hilo de segundo plano.
Usando RxJava
RxJava fue integrado en Retrofit 1 por defecto, pero en Retrofit 2 necesitas incluir algunas dependencias extra. Retrofit viene con un adaptador por defecto para ejecutar instancias Call
. Así que puedes cambiar el mecanismo de ejecución de Retrofit para incluir RxJava incluyendo el CallAdapter
de RxJava. Estos son los pasos:
Paso 1
Agrega las dependencias.
compile 'io.reactivex:rxjava:1.1.6' compile 'io.reactivex:rxandroid:1.2.1' compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
Paso 2
Agrega el nuevo CallAdapter RxJavaCallAdapterFactory.create()
cuando construyas una instancia Retrofit (línea 5).
public static Retrofit getClient(String baseUrl) { if (retrofit==null) { retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); } return retrofit; }
Paso 3
Actualiza el método APIService
savePost(String title, String body, String userId)
para que se vuelva un Observable.
@POST("/posts") @FormUrlEncoded Observable<Post> savePost(@Field("title") String title, @Field("body") String body, @Field("userId") long userId);
Paso 4
Cuando hagas las peticiones, nuestro suscriptor anónimo responde al flujo del observable que emite eventos, en nuestro caso Post
. El método onNext
es entonces llamado cuando nuestro suscriptor recibe cualquier evento, que entonces es pasado a nuestro método showResponse(String response)
.
public void sendPost(String title, String body) { // RxJava mAPIService.savePost(title, body, 1).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<Post>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Post post) { showResponse(post.toString()); } }); }
Revisa Comenzando Con ReactiveX en Android por Ashraff Hathibelagal para aprender más acerca de RxJava y RxAndroid.
10. Probando la Aplicación
En este punto, puedes ejecutar la aplicación y dar clic al botón enviar cuando hayas ingresado un título y cuerpo. La respuesta de la API se mostrará debajo del botón enviar.



11. Ejecutando una petición PUT
Ahora que sabemos cómo ejecutar una petición POST
, veamos cómo podemos ejecutar una petición PUT
que actualice entidades. Agrega el siguiente método nuevo a la clase APIService
.
@PUT("/posts/{id}") @FormUrlEncoded Call<Post> updatePost(@Path("id") long id, @Field("title") String title, @Field("body") String body, @Field("userId") long userId);
Para actualizar un post desde la API, tenemos el punto final /posts/{id}
con {id}
siendo un contenedor para el id del post que queremos actualizar. La anotación @Path
es el reemplazo nombrado en un segmento de ruta URL {id}
. Ten en cuenta que los valores son convertidos a string usando String.valueOf(Object)
y codificados en URL. Si el valor ya está codificado, puedes deshabilitar el codificado URL así: @Path(value="name", encoded=true)
.
12. Ejecutando una petición DELETE
También veamos cómo ejecutar una petición DELETE
. Usando la API JSONPlaceholder, para borrar un recurso de post, el punto final requerido es /posts/{id}
con el método HTTP DELETE
. De vuelta a nuestra interfaz APIService
, solo necesitamos incluir el método deletePost()
que lo ejecutará. Pasamos el id del post al método, y es reemplazado en el segmento de ruta URL {id}
.
@DELETE("/posts/{id}") Call<Post> deletePost(@Path("id") long id);
13. Creando una Petición
Digamos que quieres darle a tus usuarios la habilidad de cancelar o abortar una petición. Esto es muy sencillo de hacer en Retrofit. La clase Retrofit Call
tiene un método llamado cancel()
que hará justo eso (línea 30 abajo). Este método disparará el método onFailure()
en el callback.
Este método puede ser llamado, por ejemplo, si no hay conexión a internet o cuando una excepción inesperada ocurre creando la petición o manejando la respuesta. Así que para saber si la petición fue cancelada, usa el método isCanceled()
en la clase Call
(línea 18).
private Call<Post> mCall; ... public sendPost(String title, String body) { mCall = mAPIService.savePost(title, body, 1); mCall.enqueue(new Callback<Post>() { @Override public void onResponse(Call<Post> call, Response<Post> response) { if(response.isSuccessful()) { showResponse(response.body().toString()); Log.i(TAG, "post submitted to API." + response.body().toString()); } } @Override public void onFailure(Call<Post> call, Throwable t) { if(call.isCanceled()) { Log.e(TAG, "request was aborted"); }else { Log.e(TAG, "Unable to submit post to API."); } showErrorMessage(); } }); } public void cancelRequest() { mCall.cancel(); }
Conclusión
En este tutorial, aprendiste sobre Retrofit: por qué deberías usarlo y cómo integrarlo en tu proyecto para realizar peticiones POST
, PUT
, DELETE
y cancel. También aprendiste cómo integrar RxJava con este. En mi siguiente artículo sobre usar Retrofit, te mostraré cómo subir archivos.
Para aprender más sobre Retrofit, refiérete a la documentación oficial. ¡Y revisa algunos de nuestros otros tutoriales y cursos sobre desarrollo Android aquí en Envato Tuts+!