Software">
APUNTES UT11 PMDM11
APUNTES UT11 PMDM11
APUNTES UT11 PMDM11
WEB SERVICES
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.*.
● java.net.HttpURLConnection
● java.net.URL
● java.net.URLEncoder
● 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>
<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;
@Override
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:
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:
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).
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).
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.
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.
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.
<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;
@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{
//
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;
});
}
//
● 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...).
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;
@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;
@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;
@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:*/
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;
@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().
NOTA: Para acceder a esta imagen no es necesario usar una API_key aunque
puedes conseguirla registrándote con tu correo electrónico.
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;
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;
});
}
}