Software">
Nothing Special   »   [go: up one dir, main page]

APUNTES UT11 PMDM11

Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 47

APUNTES UT11 PMDM11

WEB SERVICES

MDM11.- Web Services.


1.- HttpURLConnection.
HttpURLConnection es una clase específica que a través del protocolo HTTP
permite a los sistemas clientes (habitualmente navegadores, aunque en nuestro
caso, el propio dispositivo Android será el cliente sin tener que mediar un navegador
para acceder al contenido) acceder al contenido de páginas web.

Los navegadores hacen uso de la primitiva o método GET de HTTP para


descargarse una página web del servidor. Es decir, cuando en un navegador web
cargamos la URL: https://dominio.com/dir/pagina.html, internamente se está
haciendo un GET dominio.com/dir/pagina.html. El servidor recibe esta petición y envía
al cliente la página en formato HTML.

De la misma forma, nuestro dispositivo Android, con las librerías adecuadas, será
capaz de reproducir directamente el proceso anterior. Dichas librerías
principalmente son de dos grupos, las librerías java.net.* y las librerías
org.apache.commons.httpclient.*.

Veremos un ejemplo de uso de HttpURLConnection, que pertenece al primer grupo


de las librerías anteriores. Concretamente, usaremos las clases:

● java.net.HttpURLConnection
● java.net.URL
● java.net.URLEncoder

1.1.- Ejercicio resuelto 1


(HttpURLConnection).
Realizaremos una aplicación que solicitará al usuario una URL (incluyendo el
protocolo) y se conectará a ella para devolver el resultado HTTP de la misma. Te
mostramos en la imagen cómo quedaría la aplicación con estos ejemplos:

● https://www.educa2.madrid.org/educamadrid/" OK 200
● https://www.educa2.madrid.org/educamadridNO/" No encontrado 404
● https://correoweb.educa.madrid.org/test Forbidden 403

Es importante que conozcas los distintos códigos de respuesta del protocolo HTTP
.
NOTA: Debes tener en cuenta, que en ocasiones, dependiendo de la calidad del
certificado del servidor al que te conectes puedes recibir códigos de estado no
esperados.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!--DAMOS PERMISOS DE INTERNET-->
<uses-permission android:name="android.permission.INTERNET"/>

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.HTTPURLConnectionUT11"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Verificador de URLs"
android:textSize="25dp"
android:textColor="@color/purple_700"
android:textAlignment="center"/>

<EditText
android:id="@+id/et1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="URL a verificar"
android:minHeight="48dp"
android:text="https://www.educa2.madrid.org/educamadrid/" />

<Button android:id="@+id/btn1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android: > android:text="Resultado HTTP"/>

<TextView android:id="@+id/tv1"
android:layout_height="match_parent"
android:layout_width="match_parent"/>

</LinearLayout>

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.StrictMode;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import java.net.HttpURLConnection;
import java.net.URL;

public class MainActivity extends AppCompatActivity {


private EditText et1;
private TextView tv1;

@Override

protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et1 = (EditText) findViewById(R.id.et1);
tv1 = (TextView) findViewById(R.id.tv1);
//Con la siguiente línea permitiremos que se pueda acceder a la
red desde el hilo principal (ya que no usamos AsyncTask)
StrictMode.setThreadPolicy(new
StrictMode.ThreadPolicy.Builder().permitNetwork().build());
}

public void buscar(View view) {


try {
String urlBusqueda = et1.getText().toString();
String resultado = resultadosHTTP(urlBusqueda);
tv1.append("Respuesta HTTP: " + resultado + "\n");
} catch (Exception e) {
tv1.append("Error al conectar\n");
Log.e("HTTP", e.getMessage(), e);
}
}

String resultadosHTTP(String urlABuscar) throws Exception {


String resultado = "No conectado";
try {
//URL url = new URL("https://google.es", "UTF-8") + "\");
URL url = new URL(urlABuscar);
//Preparamos la conexíon con la clase HttpURLConnection
HttpURLConnection conexion = (HttpURLConnection)
url.openConnection();
//Establecemos cabeceras HTTP. Podemos asignar a la clave
"User-Agent" parámetros de un navegador como
//"Mozilla/5.0 (Windows NT 6.1)", pero si lo dekjamos vacío
(""), también funciona.
conexion.setRequestProperty("User-Agent", "");
//Con el método getResposeCode() estableceremos la conexíon
y devuelve el resultado
resultado = String.valueOf(conexion.getResponseCode());
} catch (Exception e) {
tv1.append("Error en la URL\n");
Log.e("HTTP", e.getMessage(), e);
}
return resultado;
}
}
2.- REST.
De la Wikipedia:

La transferencia de estado representacional (en inglés representational state


transfer) o ReST es un estilo de arquitectura software para sistemas hipermedia
distribuidos como la World Wide Web. El término se originó en el año 2000, en una
tesis doctoral sobre la web escrita por Roy Fielding, uno de los principales autores
de la especificación del protocolo HTTP, y ha pasado a ser ampliamente utilizado
por la comunidad de desarrollo.

REST describe cualquier interfaz entre sistemas que utilice directamente HTTP para
obtener datos o indicar la ejecución de operaciones sobre los datos en cualquier
formato (XML, JSON, etc.), sin las abstracciones adicionales de los protocolos
basados en patrones de intercambio de mensajes, como por ejemplo SOAP.

REST afirma que la web ha disfrutado de escalabilidad como resultado de una serie
de diseños fundamentales clave:

● Un protocolo cliente/servidor sin estado: cada mensaje HTTP contiene toda


la información necesaria para comprender la petición. Como resultado, ni el
cliente ni el servidor necesitan recordar ningún estado de las comunicaciones
entre mensajes. Sin embargo, en la práctica, muchas aplicaciones basadas
en HTTP utilizan cookies y otros mecanismos para mantener el estado de la
sesión (algunas de estas prácticas, como la reescritura de URLs, no son
permitidas por REST)
● Un conjunto de operaciones bien definidas que se aplican a todos los
recursos de información: HTTP en sí define un conjunto pequeño de
operaciones, las más importantes son POST, GET, PUT y DELETE. Con
frecuencia estas operaciones se equiparan a las operaciones CRUD en
bases de datos (CLAB en castellano: crear, leer, actualizar, borrar) que se
requieren para la persistencia de datos, aunque POST no encaja
exactamente en este esquema.
● Una sintaxis universal para identificar los recursos. En un sistema REST,
cada recurso es direccionable únicamente a través de su URI.
● El uso de hipermedios, tanto para la información de la aplicación como para
las transiciones de estado de la aplicación: la representación de este estado
en un sistema REST son típicamente HTML o XML. Como resultado de esto,
es posible navegar de un recurso REST a muchos otros simplemente
siguiendo enlaces sin requerir el uso de registros u otra infraestructura
adicional

Debes conocer
Existen diversos mecanismos para consumir un servicio web. Los más
popularizados son SOAP y REST. Por simplificación, en esta unidad veremos
sólamente REST, aunque es importante que conozcas las diferencias entre
ambos mecanismos, puede que en algún momento tengas que utilizarlas. Las
diferencias principales entre SOAP y REST son:

● SOAP significa Protocolo simple de acceso a objetos, mientras que


REST significa Transferencia de estado representacional.
● SOAP es un protocolo mientras que REST es un patrón arquitectónico.
● SOAP usa interfaces de servicio para exponer su funcionalidad a las
aplicaciones cliente, mientras que REST usa localizadores de servicios
uniformes para acceder a los componentes en el dispositivo de hardware.
● SOAP necesita más ancho de banda para su uso, mientras que REST no
necesita mucho ancho de banda.
● Comparando SOAP vs REST API, SOAP solo funciona con formatos
XML, mientras que REST funciona con texto sin formato, XML, HTML y
JSON.SOAP no puede hacer uso de REST mientras que REST puede
hacer uso de SOAP.

2.1.- Ejercicio resuelto 2 (Fuentes de


datos REST).
Existen múltiples fuentes de datos que proporcionan servicio web mediante REST.

Una de ellas es: https://openexchangerates.org

Si nos conectamos a dicha URL (figura 1), bajando en esa ventana vemos que hay
un botón para obtener un ID de uso, ya que es habitual que para evitar abusos de
utilización haya que registrarse (algunos otros servicios no requieren identificación ni
API key) (figura 2). Utilizaremos el plan gratuito (figura 3).

Figura 1. Acceso Open Exchange Rates


Figura 2. Obtención de ID.

Figura 3. Plan gratuito.


Nos registramos (figura 4). Una vez validados, podemos entrar en nuestro escritorio
(figura 5) y buscar la App ID que tenemos (figura 6).
Figura 4. Registro.

Figura 5. Acceso a escritorio.


Figura 6. Búsqueda de la App ID
Ahora tendremos que buscar API Endpoints. Para ello, vamos al enlace "Getting
Started" de la ventana anterior, abriéndose la ventana mostrada en la figura 7. De
entre las API EndPoints cogemos, por ejemplo, la primera (/latest.json) (figura 8).
Dicha página describe como se utiliza (definición y parámetros que admite) dicha
API EndPoint.
Figura 7. Enlace "Getting Started"

Figura 8. Seleccionamos /latest.json


En la parte de abajo podemos probarla, para lo cual debemos suministrar
obligatoriamente la App ID anteriormente mencionada (figura 9) y darle a "Try It!"
(figura 10):
Figura 9. Suministrar la App ID
Figura 10. Try it!
Podemos ver que se genera la última valoración (ver que la fecha coincide con la
del día actual en el que hacemos la petición) de cambio del dólar (base) con
respecto a las demás monedas, usando formato JSON.

Además podemos poner la URL en un navegador e igualmente aparecerán los


resultados en formato JSON (figura 11). Dicho fichero será descargable, pero en
todo caso será preferible mantener como fuente de datos al servidor web de
openexchagerates.org para asegurarnos la puntual actualización de los datos.

Si ahora intentamos cambiar la base a euros (EUR), vemos que


desafortunadamente nos da un mensaje de error diciendo que el cambio de base
está reservado a planes de pago, por lo cual el único cambio de base que podemos
hacer es el de dólar americano (USD) a todo lo demás (figura 12).

En todo caso, este problema no es insalvable ya que será posible hacer el cambio a
través de un simple algoritmo matemático, partiendo del cambio de dólar americano
a todo lo demás, para poder hacer cualquier conversión posible.
Podría igualmente llevar la URL anterior a aplicaciones como SoapUI para probar el
acceso al WebService anterior (figura 13).

Figura 11. Resultados en formato JSON.

Figura 12. Cambio de la base a euros.


Figura 13. Uso de SoapUI.
Como vemos, este es un ejemplo de como somos capaces de acceder a servicios
REST provistos por web services externos. Hay muchos más casos, solo es
cuestión de investigar en Internet. En todo caso, el consumo de estos web services
es algo complicado, pero gracias a ciertas librerías que veremos más adelante, el
acceso y manipulación de datos será más sencillo. Concretamente veremos Retrofit.

3.- Consumo de un Web Service.


Un web service por tanto no es más que una forma de estructurar un conjunto de
acciones que permiten a las aplicaciones interactuar con un servidor para consultar
datos o modificarlos mediante los métodos del protocolo HTTP.

Para desarrollar la parte servidor (el web service en sí) puede usarse cualquier
tecnología capaz de publicar información mediante HTTP (PHP, ASP.NET, Java +
TOMAT, etc.). Para "usar" o "consumir" este web service desarrollado con REST es
necesario establecer conexiones HTTP mediante las aplicaciones. Es decir, las
aplicaciones se conectan a Internet (sin usar un navegador) y son capaces de
descargar e incluso subir datos al servidor.

Web service en el servidor


Para desarrollar la parte de servidor existen herramientas web sobradamente
preparadas que proporcionan un servicio web completamente fiable. Estará
especialmente indicado la instalación del paquete XAMPP, WAMP, o LAMP,
dependiendo de la distribución y del sistema operativo. XAMPP, por ejemplo, es un
paquete de software libre que proporciona una serie de aplicaciones de alto nivel,
entre las cuales destaca el servidor web Apache, el sistema de gestión de bases de
datos MySQL y los intérpretes para lenguajes de script PHP y Perl. El nombre
proviene del acrónimo de las aplicaciones anteriormente indicadas para cualquier
sistema operativo: X ( se refiere a cualquiera de los diferentes sistemas operativos
más utilizados), Apache, MariaDB/MySQL, PHP, Perl.

A partir de la versión 5.6.15, XAMPP cambió la base de datos MySQL por MariaDB,
la cual es una versión actualizada de MySQL con licencia GPL, y que surgió con el
propósito de garantizar que los usuarios finales pudieran seguir haciendo uso de un
sistema gestor de bases de datos avanzado, cosa que quedaba en entredicho por la
amenaza de que Oracle, propietaria de MySQL pudiera comenzar a distribuir este
SGBD bajo licencias no libres.

El programa proporciona un servidor web libre de altas prestaciones y muy fiable,


fácil de usar y capaz de interpretar páginas dinámicas. Actualmente, XAMPP está
disponible para Windows, Linux, y OS X. El XAMPP podemos instalarlo tanto en un
equipo local, como en un sistema remoto.

Los certificados de servidor


Uno de los aspectos en los que más está evolucionando Android, según avanzan
las APIs, es la seguridad. Esto ha hecho que desde hace tiempo las aplicaciones
sólo tienen disponible conexiones a servidores HTTPS, es decir, a servidores que
cuentan con un certificado que garantiza la encriptación de la comunicación.
Conseguir esto en un servidor Apache es algo sencillo porque existen algunas
formas de instalar lo que se denominan certificados autofirmados que permiten este
cifrado en las comunicaciones. Sin embargo, las restricciones de Android desde la
API 28 han aumentado, y ahora las aplicaciones no tienen habilitado el acceso a
ningún servicio web que no ofrezca HTTPS con un certificado avalado por una CA
(autoridad certificadora) que asegure que el certificado pertenece a la empresa o
persona que dice ser. Esto es necesario incluso cuando accedemos a direcciones IP
de nuestra propia red o incluso a nuestra máquina anfitriona desde nuestro Android
Studio.

Estas nuevas medidas complican la posibilidades de realizar aplicaciones que


consuman servicios web desarrollados por nosotros mismos ya que es necesario
instalar certificados reales en el servidor o crear entidades certificadoras, lo cual
excede de los objetivos del módulo.
3.1.- Ejercicio resuelto 3 (Consumo de un
Web Service).

Ejercicio Resuelto
A partir del web service público con el que trabajamos en la unidad anterior
(https://openexchangerates.org/api/latest.json ) que devuelve un conjunto de
divisas y los valores de cambio de las mismas con respecto al dólar (USD)
vamos a realizar una aplicación capaz de hablar con
este servicio para que calcule el número de divisas
disponibles.

Para realizar el ejercicio, la aplicación solicitará al


usuario el ID de API que debes conseguir tal como se
explicó en el ejercicio anterior. En caso de que este
no sea correcto, la aplicación devolverá un mensaje
de error a través de TOAST. En caso de que pueda
conectar, la aplicación simplemente contará las
divisas que tiene disponibles y mostrará el JSON
devuelto por el servicio web.

Este es el aspecto que tendrá la aplicación antes y


después de establecer la conexión con el servicio.

Antes de establecer la conexión


con el servicio.

Después de establecer la conexión con el servicio.

Nota: Puedes encontrar cualquier imagen relacionada con las


divisas en Internet para usarla como recurso "drawable".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

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

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.WebServiceUT11"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<TextView
android:id="@+id/tvTitulo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Acceso a openexchangerates.org"
android:textSize="20dp"
tools:text="API ID"
android:layout_margin="15dp"/>

<ImageView
android:id="@+id/imgvLogin"
android:layout_width="match_parent"
android:layout_height="80dp"
app:srcCompat="@drawable/divisas"
android:layout_margin="20dp"/>

<EditText
android:id="@+id/etAPIID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Introduce tu API ID"/>

<Button
android:id="@+id/btnLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Calcular divisas disponibles"
android:layout_margin="15dp"/>

<TextView
android:id="@+id/tvDivisasEncontradas"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20dp"
android:layout_margin="15dp"/>
<TextView
android:id="@+id/tvJsonSent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="12dp"
android:layout_margin="5dp"/>
</LinearLayout>
package com.cherryreynoso.webserviceut11;

import android.os.Bundle;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

//IMPORTO
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;

public class MainActivity extends AppCompatActivity {


EditText etAPIID;
TextView tvDivisasEncontradas, tvJsonSent;
Button btnLogin;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
//
etAPIID=(EditText)findViewById(R.id.etAPIID);
tvDivisasEncontradas
=(TextView)findViewById(R.id.tvDivisasEncontradas);
tvJsonSent = (TextView)findViewById(R.id.tvJsonSent);
btnLogin=(Button)findViewById(R.id.btnLogin);
btnLogin.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View view) {
//Creamos un hilo para conectar probando la APIID
Thread hilo=new Thread(){
@Override
public void run() {
super.run();
final String
respuesta=enviarPost(etAPIID.getText().toString());
runOnUiThread(new Runnable() { //El método
runOnUiThread me permite trabajar con la interfaz gráfica desde
detro del hilo
@Override
public void run() {
//Limpiamos por si se repite la
acción
tvDivisasEncontradas.setText("");
tvJsonSent.setText("");

//Toast.makeText(getApplicationContext(), respuesta,
Toast.LENGTH_LONG).show();
int numElementosEncontrados =
objetoJSON(respuesta);
if (numElementosEncontrados>0){

tvDivisasEncontradas.setText("Divisas disponibles:" +
Integer.toString(numElementosEncontrados) + "\n" +
"JSon de Respuesta:");
tvJsonSent.setText(respuesta);
}
else{

Toast.makeText(getApplicationContext(), "La consulta no devolvió


divisas. verifique API ID", Toast.LENGTH_LONG).show();
}
}
});
}
};
hilo.start(); //Con esto arrancamos el hilo
}
});

//

ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main),
(v, insets) -> {
Insets systemBars =
insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top,
systemBars.right, systemBars.bottom);
return insets;
});
}

//

public String enviarPost(String app_id){


String parametros="app_id="+app_id; //Los literales
corresponden a los parámetros que se pasan en la URL a la app
HttpURLConnection conexion=null;
String respuesta="";
try{
//Tenemos que consultar la página enviándole los datos de
mi aplicación (correo y password) como parámetros
URL url=new
URL("https://openexchangerates.org/api/latest.json" + "?" +
parametros);
conexion=(HttpURLConnection)url.openConnection();
conexion.setRequestMethod("POST");
conexion.setRequestProperty("Content-Length", "");
conexion.setDoOutput(true);
DataOutputStream dos=new
DataOutputStream((conexion.getOutputStream()));
dos.close();
//Recogeremos la respuesta que nos devuelve el WebService
y la recorremos para cargarla en la cadena que devolverá este método
(corresponde al JSON devuelto)
Scanner lectura=new Scanner(conexion.getInputStream());
while (lectura.hasNextLine())
respuesta += lectura.nextLine();
}catch (Exception e){
Log.e("Error",e.toString());
}
return respuesta.toString();
}

//Creamos un método para contar cuantas divisas devuelve la


consulta
//Pasaremos como parámetro la respuesta que da el métooo
enviarPost() anterior
public int objetoJSON(String respuesta){
int numDivisas = 0;
try{
JSONObject jObject = new JSONObject(respuesta);
JSONObject divisas = jObject.getJSONObject("rates");
numDivisas = divisas.length();
//Log.d("Divisas", Integer.toString(numDivisas));
}catch (Exception e){
Log.e("Error", e.getMessage());
}
return numDivisas;
}
}
4.- Retrofit.
Retrofit es una tecnología basada en REST con la cual podremos desarrollar
aplicaciones en Android que accedan a servicios web de una forma relativamente
sencilla.

Para usar esta tecnología debemos incorporar dos librerías:

● La librería Retrofit para Android y Java (compatible con Kotlin) para hacer
llamadas de red y obtener el resultado.
● Una librería que nos permita convertir el resultado devuelto “parseando” de
forma automática a su objeto; esto facilita mucho realizar peticiones a un API
y procesar la respuesta.

Dispone de métodos que manejan API REST con contenidos en formato XML o en
formato JSON. Retrofit puede manejar las clásicas primitivas de HTTP (GET, POST,
PUT, HEAD...).

El resultado de la consulta al servicio web se incorpora en nuestra aplicación en una


estructura de clases que representen los objetos de la respuesta esperada en
formato POJO. Estas clases las debemos crear nosotros mismos con la misma
estructura que tenga el resultado de la respuesta. Por ejemplo, si la respuesta es en
formato JSON, deberemos construir una estructura de clases que represente los
campos que nos interesan en la consulta de este JSON.

En este enlace puedes aprender más sobre el cliente HTTP de Retrofit

En este otro enlace puedes ver un ejemplo en Kotlin y algunas explicaciones de


cómo obtener datos de Internet desde un cliente Retrofit en una aplicación de
Android
build.gradle (Module app)
En primer lugar, incorporaremos en build.gradle (Module app) las dependencias:
dependencies {
...
// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
// Retrofit with Convertes gson
implementation "com.squareup.retrofit2:converter-
gson:2.9.0"}

Actualizado://
https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit
implementation("com.squareup.retrofit2:retrofit:2.11.0")

//
https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-
gson
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
AndroidManifest.xml
No debemos olvidar configurar el permiso de acceso a Internet en el fichero
AndroidManifest.xml:

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

Por otro lado, dentro de la carpeta modelos hemos creado una serie de clases
auxiliares que representan el formato de los objetos que vamos a recibir del web
service en formato JSON. El formato esperado en el momento de redactar este
ejercicio es el mostrado a continuación. Si hubiera cambios tendrás que adaptar los
nombres de los atributos de las clases usadas e incluso la estructura de los archivos
para que sea similar a la estructura del JSON recibido.
package com.cherryreynoso.retrofitut11.Modelo;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class CambioDivisas {


@SerializedName("timestamp")
@Expose
private String timestamp;
@SerializedName("base")
@Expose
private String base;
@SerializedName("rates")
@Expose
private Rates rates;

public String getTimestamp() {


return timestamp;
}

public void setTimestamp(String timestamp) {


this.timestamp = timestamp;
}

public String getBase() {


return base;
}

public void setBase(String base) {


this.base = base;
}
public Rates getRates() {
return rates;
}

public void setRates(Rates rates) {


this.rates = rates;
}

@Override
public String toString() {
return "CambioDivisas{" +
"timestamp='" + timestamp + '\'' +
", base='" + base + '\'' +
", rates=" + rates +
'}';
}
}

package com.cherryreynoso.retrofitut11.Modelo;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Rates {


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

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

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

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

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

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

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

public String getEUR() {


return EUR;
}

public void setEUR(String EUR) {


this.EUR = EUR;
}

public String getAUD() {


return AUD;
}

public void setAUD(String AUD) {


this.AUD = AUD;
}

public String getCNY() {


return CNY;
}

public void setCNY(String CNY) {


this.CNY = CNY;
}

public String getGBP() {


return GBP;
}

public void setGBP(String GBP) {


this.GBP = GBP;
}

public String getISK() {


return ISK;
}

public void setISK(String ISK) {


this.ISK = ISK;
}

public String getMXN() {


return MXN;
}

public void setMXN(String MXN) {


this.MXN = MXN;
}

public String getPLN() {


return PLN;
}

public void setPLN(String PLN) {


this.PLN = PLN;
}

@Override
public String toString() {
return "Rates{" +
"EUR='" + EUR + '\'' +
", AUD='" + AUD + '\'' +
", CNY='" + CNY + '\'' +
", GBP='" + GBP + '\'' +
", ISK='" + ISK + '\'' +
", MXN='" + MXN + '\'' +
", PLN='" + PLN + '\'' +
'}';
}
}

INTERFACE
package com.cherryreynoso.retrofitut11;

import com.cherryreynoso.retrofitut11.Modelo.CambioDivisas;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Query;
/**Además, para configurar la conexión al servidor web, tenemos la
interfaz que conecta con el servicio web
* que mediante el método GET solicita la aplicación llamada
latest.json pasando el parámetro app_id.
* Si quisiéramos consumir este servicio desde un navegador,
tendríamos que usar esta URL con tu ID de
* API https://openexchangerates.org/api/latest.json?
app_id=8b2c865cad6exxxxx...,
* pero con Retrofit debemos construirla de la siguiente manera:*/

public interface DivisasAPI {


String BASE_URL = "https://openexchangerates.org/api/";
@Headers("Content-type: application/json")
@GET("latest.json")
Call<CambioDivisas> getStuff(@Query("app_id") String key);
}

MAIN

package com.cherryreynoso.retrofitut11;

import android.os.Bundle;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

//
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.cherryreynoso.retrofitut11.Modelo.CambioDivisas;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity {


private static final String TAG = "MainActivity";
private static String BASE_URL =
"https://openexchangerates.org/api/";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
//
Button btnGetData = (Button) findViewById(R.id.btnGetData);
EditText etAPIID = (EditText) findViewById(R.id.etAPIID);
TextView tvBase = (TextView) findViewById(R.id.tvBase);
TextView tvConvert = (TextView) findViewById(R.id.tvConvert);

btnGetData.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View view) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(DivisasAPI.BASE_URL)
.addConverterFactory(GsonConverterFactory.cre
ate())
.build();

DivisasAPI divisasAPI =
retrofit.create(DivisasAPI.class);
Call<CambioDivisas> call =
divisasAPI.getStuff(etAPIID.getText().toString());

call.enqueue(new Callback<CambioDivisas>() {

@Override
public void onResponse
(Call<CambioDivisas> call,
Response<CambioDivisas> response) {

tvBase.setText("Referencia 1" +
response.body().getBase());
tvConvert.setText("EUR : " +
response.body().getRates().getEUR() + "\n" +
"AUD : " +
response.body().getRates().getAUD() + "\n" +
"CNY : " +
response.body().getRates().getCNY() + "\n" +
"ISK : " +
response.body().getRates().getISK() + "\n" +
"MXN : " +
response.body().getRates().getMXN() + "\n" +
"PLN : " +
response.body().getRates().getPLN() + "\n");
//Trazas
//Log.d(TAG, "onResponse: Respuesta del
servidor; " + response.toString());
//Log.d(TAG, "onResponse: Info recibida; " +
response.body().toString());
}

@Override
public void onFailure(Call<CambioDivisas> call,
Throwable t) {
//Log.e(TAG, "Error: " + "onFailure: Algo
salio mal:" + t.getMessage());
Toast.makeText(MainActivity.this, "Algo salio
mal:", Toast.LENGTH_SHORT).show();
}
});
}
});
//

ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main),
(v, insets) -> {
Insets systemBars =
insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top,
systemBars.right, systemBars.bottom);
return insets;
});
}
}
5.- Obtención de imágenes a través de la
web.
Existen varias librerías que permiten descargarnos imágenes procedentes de
servidores web. Las más conocidas son Picasso, Glide, Universal Image Loader y
Fresco. Además, incluso Retrofit dispone de métodos relacionados con la carga de
imágenes como getImage().

La librería Picasso se caracteriza por su sencillez y poco código necesario para


poder cargar imágenes con buena calidad (por ejemplo la calidad de imagen que
maneja Glide es algo peor, pero a diferencia de Picasso, Glide soporta formato GIF
animados). Será necesario incluir la librería a través de la inclusión en el apartado
de dependencias del build.gradle (Module: app) como hemos hecho con otras
librerías, en el apartado de las dependencias.

Aquí puedes encontrar más detalles sobre la librería Picasso .

5.1.- Ejercicio resuelto 5 (Imágenes con


Picasso).
Se pide, usando la librería Picasso, conectar con una API de la NASA que publica
imágenes de Marte capturadas por el Rover Curiosity.

NOTA: Para acceder a esta imagen no es necesario usar una API_key aunque
puedes conseguirla registrándote con tu correo electrónico.

Las APIs están publicadas en esta página web: https://api.nasa.gov/

Como puedes ver en la figura 1, debes ir a la página, pulsar el botón de la parte


superior de "Browse APIs" y, a continuación, desplazarnos hasta abajo y encontrar
los detalles de la API "Mars Rover Photos".

En la figura 2 vemos que pulsando al primer enlace (https://api.nasa.gov/mars-


photos/api/v1/rovers/curiosity/photos?sol=1000&api_key=DEMO_KEY ) de
"Example Queries" podemos ver la estructura del JSON devuelto por el servicio.

Desarrolla una App Android que muestre, mediante la librería Picasso, tres de estas
imágenes servidas desde estas páginas. Para ello puedes conseguir los enlaces
mostrados en alguna de las imágenes publicadas del JSON. El aspecto de la
aplicación debe ser el mostrado en la figura 3.

Figura 1
Figura 2
build.gradle module.app
dependencies {

implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
implementation("com.squareup.picasso:picasso:2.8")

android manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

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

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PicassoUT11"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
activity_main
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/iv1"
android:layout_width="162dp"
android:layout_height="125dp"
tools:layout_constraintTop_creator="1"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
tools:layout_constraintLeft_creator="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginLeft="8dp" />

<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nasa - Curiosity - Img0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.725"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.081" />

<ImageView
android:id="@+id/iv2"
android:layout_width="162dp"
android:layout_height="125dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="180dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_constraintLeft_creator="1"
tools:layout_constraintTop_creator="1" />

<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nasa - Curiosity - Img10"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.748"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.324" />

<ImageView
android:id="@+id/iv3"
android:layout_width="162dp"
android:layout_height="125dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="337dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_constraintLeft_creator="1"
tools:layout_constraintTop_creator="1" />

<TextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nasa - Curiosity - Img20"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.748"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.561" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity
package com.cherryreynoso.picassout11;

import android.os.Bundle;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {


ImageView iv1;
ImageView iv2;
ImageView iv3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
//
iv1=(ImageView)findViewById((R.id.iv1));
iv2=(ImageView)findViewById((R.id.iv2));
iv3=(ImageView)findViewById((R.id.iv3));
//URL de la API
https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?
sol=1000&api_key=DEMO_KEY

Picasso.get()
.load
("https://mars.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/
sol/01000/opgs/edr/fcam/FLB_486265257EDR_F0481570FHAZ00323M_.JPG")
.resize(500, 500)
.centerCrop()
.into(iv1);
Picasso.get()
.load
("https://mars.nasa.gov/msl-raw-images/msss/01000/mcam/1000MR0044631
270503687E03_DXXX.jpg")
.resize(500, 500)
.centerCrop()
.into(iv2);
Picasso.get()
.load
("https://mars.nasa.gov/msl-raw-images/msss/01000/mcam/1000MR0044631
220503682E01_DXXX.jpg")
.resize(500, 500)
.centerCrop()
.into(iv3);
/* Otra forma de llamar aun mas corta

Picasso.get().load("https://mars.nasa.gov/msl-raw-images/proj/msl/
redops/ods/surface/sol/01000/opgs/edr/fcam/
FLB_486265257EDR_F0481570FHAZ00323M_.JPG").into(iv1); */
//

ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main),
(v, insets) -> {
Insets systemBars =
insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top,
systemBars.right, systemBars.bottom);
return insets;
});
}
}

También podría gustarte