Trabajando con servicios REST - jpexposito/android GitHub Wiki

Establecer permisos

Haciendo uso de una REST API, obviamente implica usar Internet. Sin embargo, las aplicaciones Android pueden acceder a Interne solamente si tienen el permiso android.permission.INTERNET. Por lo tanto, antes de que empiece a escribir cualquier código de red, debe asegurarse que la siguiente etiqueta; uses-permission está presente en su archivo de manifiesto de su proyecto.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Cómo elegir un cliente HTTP

La mayoría de las apps conectadas a redes utilizan HTTP para enviar y recibir datos. La plataforma de Android incluye el cliente HttpsURLConnection, que admite TLS, cargas y descargas de transmisión, tiempos de espera configurables, IPv6 y reducciones de conexiones. En este tema, se usa la biblioteca cliente HTTP de Retrofit, que te permite crear un cliente HTTP de forma declarativa. Además, Retrofit admite la serialización de cuerpos de solicitud y la deserialización de cuerpos de respuesta automáticas.

HttpsURLConnection

Trabajando con una conexión HTTP

Usando el método openConnection() de la clase URL, usted puede fácilmente configurar una conexión a cualquier punto final de REST. El valor de retorno de openConnection() debe ser emitido por una instancia o ya sea HttpURLConnection o HttpsURLConnection, dependiendo de si el punto final puede tener acceso sobre HTTP y HTTPS. Tanto HttpURLConnection como HttpsURLConnection le permiten realizar operaciones tales como añadir peticiones de encabezados y respuestas de lecturas.

    // Create URL
  URL githubEndpoint = new URL("https://api.github.com/");

  // Create connection
  HttpsURLConnection myConnection =
          (HttpsURLConnection) githubEndpoint.openConnection();
Añadir cabeceras

La mayoría de los sitios web que ofrecen REST APIs, quieren ser capaces de identificar solamente su aplicación. La forma más fácil de ayudarles a hacer eso es incluir un encabezado único User-Agent en todas sus peticiones.

  myConnection.setRequestProperty("Accept", 
          "application/vnd.github.v3+json");
  myConnection.setRequestProperty("Contact-Me", 
          "[email protected]");
Lectura de las respuestas

Una vez que haya pasado todos los encabezados de solicitudes, entonces puede revisar si tiene una respuesta valida usando el método getResponseCode() del objeto HttpURLConnection.

if (myConnection.getResponseCode() == 200) {
    // Success
    // Further processing here
} else {
    // Error handling code goes here
}

Si la clase HttpURLConnection consigue una respuesta de código redireccionada tal como 301, controla automáticamente y sigue redireccionando. Por lo tanto, normalmente, no tendrá que escribir ningún código extra pare revisar las redirecciones.

En caso de que no exista errores, puede llamar al método getInputStream() para obtener una referencia de la secuencia de entradas de la conexión.

InputStream responseBody = myConnection.getInputStream();

Como podemos ver el trabajo con la conexión __HttpsURLConnection es costosa a nivel de programación.

Retrofit

Una vez que hemos añadido los permisos de internet. el primer paso será incluir la librería Retrofit. Para ello hemos de incluirla en el ficher build.gradle.

implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.google.code.gson:gson:2.6.1'

build.gradle

  dependencies {
      "__comment": "dejar las dependencias anteriores"
      implementation 'com.squareup.retrofit2:retrofit:2.4.0'
      implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
      implementation 'com.google.code.gson:gson:2.6.1'
  }

Serializando el modelo

En este punto, y para realizar el trabajo con servicios Rest, es importante realizar la serialización del modelo. Un ejemplo será:

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class User {
    @SerializedName("id")
    @Expose
    private int id;

    @SerializedName("name")
    @Expose
    private String name;

    public User() {
    }

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Creación del servicio

El siguiente paso será realizar la creación de la interface, para atacar una restAPI:

import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;

public interface UserService {

    @GET("user/")
    Call<List<User>> getUsers();

    @POST("add/")
    Call<User> addUser(@Body User user);

    @PUT("update/{id}")
    Call<User> updateUser(@Path("id") int id, @Body User user);

    @DELETE("delete/{id}")
    Call<User> deleteUser(@Path("id") int id);
}

Es importante comprender que significan las operaciones:GET, POST, PUT, DELETE.

RetrofitClient

El siguiente paso será la creación del cliente:

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {

    private static Retrofit retrofit = null;

    public static Retrofit getClient(String url){
        if(retrofit == null){
            retrofit = new Retrofit.Builder().baseUrl(url)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }

        return retrofit;
    }

}

Creación de la APIUtils

Vamos a enmascarar el uso de la librería que utilizamos para atacar REST.

public class APIUtils {

  private APIUtils(){
  };

  public static final String API_URL = "http://169.254.35.189:8080/demo/";

  public static UserService getUserService(){
      return RetrofitClient.getClient(API_URL).create(UserService.class);
    }

}

Creación de los métodos:

Operación insertar

public void addUser(User u){
        Call<User> call = userService.addUser(u);
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if(response.isSuccessful()){
                    Toast.makeText(UserActivity.this, "User created successfully!", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                Log.e("ERROR: ", t.getMessage());
            }
        });
    }

Operación actualizar

public void updateUser(int id, User u){
        Call<User> call = userService.updateUser(id, u);
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if(response.isSuccessful()){
                    Toast.makeText(UserActivity.this, "User updated successfully!", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                Log.e("ERROR: ", t.getMessage());
            }
        });
    }

Operación eliminar

public void deleteUser(int id){
        Call<User> call = userService.deleteUser(id);
        call.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                if(response.isSuccessful()){
                    Toast.makeText(UserActivity.this, "User deleted successfully!", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                Log.e("ERROR: ", t.getMessage());
            }
        });
    }

Operación Buscar

public void getUsersList(){
        Call<List<User>> call = userService.getUsers();
        call.enqueue(new Callback<List<User>>() {
            @Override
            public void onResponse(Call<List<User>> call, Response<List<User>> response) {
                if(response.isSuccessful()){
                    list = response.body();
                    listView.setAdapter(new UserAdapter(MainActivity.this, R.layout.list_user, list));
                }
            }

            @Override
            public void onFailure(Call<List<User>> call, Throwable t) {
                Log.e("ERROR: ", t.getMessage());
            }
        });
    }

Estructura de paquetes

La estructura de paquetes para realizar este pequeño ejemplo, teniendo en cuenta que primero pondremos nuestro packege "paqueId" en el momento de la definición de la solución, es el siguiente:

  • {paqueId}.model. Donde estará la clase que será el modelo. (User.java).
  • {paqueId}.activity. Donde se alojan todos los activity de la app.
  • {paqueId}.adapter. Donde se alojan todos los adapter de la app.
  • {paqueId}.network. Donde se alojan todos las conexiones a los servicios rest de la app.

Servicio Rest

Para realizar las pruebas pertinentes puedes descargar el servicio rest que se encuentra en el siguiente enlace.

Referencias

⚠️ **GitHub.com Fallback** ⚠️