Consumir API Rest con Android + ListView

Hace unas semanas escribí un articulo acerca de implementar un ListView en android: http://equisd.com/android-listas-o-listview-facil-en-5-pasos-2019/ (Ojo para implementar Android con API Rest, es necesario leer el articulo anterior)

Recordamos de ese articulo que uno los pasos esenciales era crear un Modelo de datos, el cual nos provea registros de Contactos, Productos, Prendas, etc. Para luego mostrarlos en pila, el famoso ListView.

En ese sentido, comprendemos que los datos nos lo provee el Modelo, ya sea de una fuente de datos estático, Sqlite o desde la nube. Y si nuestro requerimiento fuese consumir datos desde la nube, lo más lógico seria crear una conexión entre la nube y el modelo.

1. Requisitos antes de trabajar con la nube

Incluimos la libreria: com.android.volley:volley:1.1.1 y acto seguido sincronizamos Android Studio.

Añadimos la libreria en build.gradle
Añadimos la libreria en build.gradle

Todo con calma y por pasos. Antes de trabajar con la nube debemos implementar unas utilidades que nos permitan interactuar con las peticiones, encolar peticiones y demás.

Es por ello que creamos un archivo en el siguiente directorio:

Creamos la clase QueueUtils.java
Creamos la clase helpers/QueueUtils.java

E implementamos lo siguiente:

import android.graphics.Bitmap;
import android.content.Context;
import android.util.LruCache;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;

public class QueueUtils {
    private static QueueObject uniqueInstance;
    public static class QueueObject {
        private RequestQueue mRequestQueue;
        private ImageLoader mImageLoader;
        private static Context mCtx;
        private QueueObject(Context context) {
            mCtx = context;
            mRequestQueue = getRequestQueue();
            mImageLoader = new ImageLoader(mRequestQueue,
                    new ImageLoader.ImageCache() {
                        private final LruCache<String, Bitmap>
                                cache = new LruCache<String, Bitmap>(20);
                        @Override
                        public Bitmap getBitmap(String url) {
                            return cache.get(url);
                        }
                        @Override
                        public void putBitmap(String url, Bitmap bitmap) {
                            cache.put(url, bitmap);
                        }
                    });
        }

        public RequestQueue getRequestQueue() {
            if (mRequestQueue == null) {
                mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
            }
            return mRequestQueue;
        }

        public <T> void addToRequestQueue(Request<T> req) {
            getRequestQueue().add(req);
        }

        public ImageLoader getImageLoader() {
            return mImageLoader;
        }
    }

    public static synchronized QueueObject getInstance(Context context) {
        if (uniqueInstance == null) {
            uniqueInstance = new QueueObject(context);
        }
        return uniqueInstance;
    }
}

No asustarnos con el código anterior pues quedaría como la siguiente imagen:

Implementamos la utilidad QueueUtils, para interactuar con la nube.
Implementamos la utilidad QueueUtils, para interactuar con la nube.

2. Conectamos el Modelo con la Nube

Teniendo la utilidad implementada, requerimos crear la función que realize las peticiones a la nube en búsqueda de datos.

Evidentemente necesitamos un endpoint de datos, para el ejemplo usaremos el siguiente: http://fipo.equisd.com/api/users.json que podemos encontrarlo en el siguiente link.

Implementamos la función de consumo de datos.
Implementamos la función de consumo de datos.
import java.util.ArrayList;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import info.rayrojas.myapplication.MainActivity;
import info.rayrojas.myapplication.helpers.QueueUtils;

public class Contacto {
    public String phone;
    public String nickname;

    public Contacto(String _phone, String _nickname) {
        this.phone = _phone;
        this.nickname = _nickname;
    }

    public static ArrayList getCollection() {
        ArrayList<Contacto> collection = new ArrayList<>();
        collection.add(new Contacto("981999923", "Bichito"));
        collection.add(new Contacto("9859913923", "Plaga"));
        collection.add(new Contacto("981914213", "Libelula"));
        return collection;
    }
    public static void injectContactsFromCloud(final QueueUtils.QueueObject o,
                                               final ArrayList<Contacto> contactos,
                                               final MainActivity _interface) {
        String url = "http://fipo.equisd.com/api/users.json";
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
                (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {

                    @Override
                    public void onResponse(JSONObject response) {
                        if (response.has("data")) {

                            try {
                                JSONArray list = response.getJSONArray("data");
                                for (int i=0; i < list.length(); i++) {
                                    JSONObject o = list.getJSONObject(i);
                                    contactos.add(new Contacto(o.getString("first_name"),
                                            o.getString("last_name")));
                                }

                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                            _interface.refreshList(); // Esta función debemos implementarla
                            // en nuestro activity
                        }
                    }
                }, new Response.ErrorListener() {

                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
                });
        o.addToRequestQueue(jsonObjectRequest);
    }
}

3. Llamamos la función desde el Activity

Felicidades, si has llegado a este punto sin problemas, ya que es el penúltimo paso para tener funcionando nuestro ListView con datos obtenidos de un API.

Tener en cuenta nuestra implementación anterior (pincha link para ver implementación anterior) que solo consideraba datos estáticos.

Implementación anterior: Android listas o listview
Implementación anterior: http://equisd.com/android-listas-o-listview-facil-en-5-pasos-2019/

A partir de la implementación anterior, modificamos nuestro código como sigue:

Implementamos nuestro activity para mostrar datos en el ListView.
Implementamos nuestro activity para mostrar datos en el ListView.
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
import info.rayrojas.myapplication.adapters.ContactoAdaptador;
import info.rayrojas.myapplication.helpers.QueueUtils;
import info.rayrojas.myapplication.models.Contacto;

public class MainActivity extends AppCompatActivity {

    ListView contactosList;
    ContactoAdaptador contactoAdaptador;
    QueueUtils.QueueObject queue = null;
    ArrayList<Contacto> items;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        contactosList = findViewById(R.id.contactosList);
        queue = QueueUtils.getInstance(this.getApplicationContext());
        items = new ArrayList<>();
        Contacto.injectContactsFromCloud(queue, items, this);
        contactoAdaptador = new ContactoAdaptador(this, items);
        contactosList.setAdapter(contactoAdaptador);
    }
    public void refreshList(){
        if ( contactoAdaptador!= null ) {
            contactoAdaptador.notifyDataSetChanged();
        }
    }
}

4. Agregamos permisos de Internet

Por último agregamos el permiso de acceso a internet en el archivo AndroidManifest.xml

Añadimos permisos de internet para consumo de datos.
Añadimos permisos de internet para consumo de datos.
Adicional agregamos: android:usesCleartextTraffic=”true”

Resultado final:

Resultado final, de implementar un ListView consumiendo datos de la nube.
Resultado final, de implementar un ListView consumiendo datos de la nube.

Hemos culminado, y tenemos nuestra aplicación consumiendo datos API Rest. En hora buena 😀

Como siempre recordarles que la base de esa implementación son los modelos que permiten la manipulación de los datos.

Espero haber ayudado en la implementación en sus proyectos, los veo en el siguiente articulo, gracias. 😀