EJERCICIO JAVA: BARRA DE MENÚS EMERGENTES

La barra de menús nos permitirá acceder a las opciones más importantes del programa. Todo programa de gran envergadura suele tener una barra de menús.


1. Veamos como añadir una barra de menús a nuestras aplicaciones. En primer lugar, crea un proyecto con el NetBeans.
2. Añade a tu ventana un objeto JMenuBar


3. En la parte superior de tu ventana aparecerá esto:


4. En el inspector (parte inferior izquierda) observarás como aparece un objeto JMenuBar, y, dentro de él, un objeto del tipo JMenu. Los objetos JMenu representan las opciones principales contenidas dentro de la barra de menús.




EJERCICIO JAVA: SCROLLBARS

Introducción a las JscrollBars (Barras de desplazamiento)
La clase JScrollBar permite crear barras de desplazamiento independientes, como la que se muestra a continuación:


La barra tiene un valor mínimo, que se consigue haciendo que el recuadro de la barra de desplazamiento esté pegado a la parte izquierda.

                                         Valor mínimo

Cuando se pulsa algunos de los botones de la barra de desplazamiento, el valor de la barra se incrementa / decrementa poco a poco. A este incremento / decremento lo llamaremos incremento unitario.

Decrementa el valor poco a poco (incremento unitario)                      Incrementa el valor poco a poco (incremento unitario                                             

Cuando se pulsa directamente sobre la barra, el valor de la barra se incrementa / decrementa en mayor cantidad. A este incremento / decremento lo llamaremos incremento en bloque.

Al pulsar directamente sobre la barra se decrementa en mayor cantidad (incremento en bloque)
Al pulsar directamente sobre la barra se incremente en mayor cantidad (incremento en bloque)

Ejercicio guiado

1. Para comprender mejor el funcionamiento de las barras de desplazamiento se creará un proyecto nuevo.
2. Añade en el proyecto una barra de desplazamiento (JScrollBar) y llámala desValor.




EJERCICIO JAVA: SPINNER

Introducción a los JSpinner
La clase JSpinner permite crear cuadros como el siguiente:


Son elementos muy comunes en los programas. A través de los dos botones triangulares se puede hacer que el valor del cuadro aumente o disminuya. También se puede escribir directamente un valor dentro del cuadro.

Ejercicio guiado

1. Crea un nuevo proyecto.
2. Añade en él un JSpinner. Su nombre será spiValor.


3. Añade una etiqueta con borde. Su nombre será etiValor.
4. La ventana tendrá el siguiente aspecto:



5. Interesa que cuando cambie el JSpinner (ya sea porque se pulsaron los botones triangulares o porque se escribió dentro) aparezca el valor correspondiente dentro de la etiqueta. Para ello, tendrá que programar el evento stateChanged del JSpinner.



EJERCICIO JAVA: SLIDERS

Introducción a los JSliders
La clase JSlider permite crear objetos como el siguiente:


Estos elementos tienen un pequeño recuadro que se puede arrastrar a derecha o izquierda. Según la posición
del recuadro, el JSlider tendrá un valor concreto.
El JSlider se puede configurar para que muestre los distintos valores que puede tomar:


También se puede configurar de forma que los valores mínimo y máximo sean distintos:


El valor que tiene un JSlider es el valor al que apunta el recuadro del JSlider. En la imagen anterior, el JSlider tiene un valor de 85.
Se verá a continuación las características más interesantes de los JSlider y como programarlos.
Ejercicio guiado
1. Crea un nuevo proyecto.
2. Añade en él un JSLider. Su nombre será slDeslizador.


EJERCICIO JAVA: TOGGLEBUTTONS

1. Realiza un nuevo proyecto.
2. Crearás una ventana como la que sigue teniendo en cuenta lo siguiente:

a. Se añadirá una etiqueta con el texto “Precio Base”. No hace falta cambiarle su nombre.

b. Se añadirá un cuadro de texto llamado txtPrecioBase.

c. Se creará un botón “Calcular”, llamado btnCalcular.

d. Se creará una etiqueta vacía y con borde llamada etiTotal. Use la propiedad font de esta etiqueta para hacer que el texto tenga un mayor tamaño.



EJERCICIO JAVA: MODELOS DE CUADRO DE LISTA

1. Realiza un nuevo proyecto.
2. En la ventana principal debes añadir lo siguiente:

a. Un combo llamado cboNumeros.
b. Un botón “Pares” llamado btnPares.
c. Un botón “Impares” llamado btnImpares.
d. Una etiqueta con borde llamada etiResultado.

3. Elimina todos los elementos que contenga el combo. Recuerda, debes usar la propiedad “model” del combo para cambiar sus elementos.

4. Después de haber hecho todo esto, tu ventana debe quedar más o menos así:

5. En el evento actionPerformed del botón Pares, programa lo siguiente:

int i;
      
DefaultComboBoxModel modelo = new DefaultComboBoxModel();
       
for (i=0;i<10;i+=2) {
   modelo.addElement("Nº "+i);
}
       
cboNumeros.setModel(modelo);


6. Observa lo que hace este código:
a. Crea un objeto “modelo” para el combo.

Al igual que pasa con los cuadros de lista, los combos tienen un objeto “modelo” que es el que realmente contiene los datos. En el caso de los combos, para crear un objeto “modelo” se usará esta instrucción:

DefaultComboBoxModel modelo = new DefaultComboBoxModel();

b. A continuación, se usa el objeto “modelo” creado y se rellena de datos. Concretamente, se rellena con los números pares comprendidos entre 0 y 10.

c. Observa el uso de la propiedad addElement para añadir un elemento al modelo del combo.

d.Se ha usado un bucle for para hacer la introducción de datos en el modelo más fácil.

e. Finalmente, se asocia el modelo al combo a través de la siguiente línea, con lo que el combo aparece relleno con los elementos del modelo:

cboNumeros.setModel(modelo);

7. Ejecuta el programa y observa el funcionamiento del botón Pares.



8. El botón Impares es similar. Programa su actionPerformed como sigue

int i;
DefaultComboBoxModel modelo = new DefaultComboBoxModel();
       
for (i=1;i<10;i+=2) {
   modelo.addElement("Nº "+i);
}
       
cboNumeros.setModel(modelo);

9. La única diferencia de este código es el for, que está diseñado para que se introduzcan los números impares comprendidos entre 0 y 10 dentro del modelo.
10. Finalmente se programará el actionPerformed del combo para que al seleccionar un elemento este aparezca en la etiqueta. Esto se hace con una simple instrucción:

etiResultado.setText(cboNumeros.getSelectedItem().toString());

Recuerda el uso de getSelectedItem() para recoger el elemento seleccionado, y el uso de toString() para convertirlo a texto.
11. Prueba el programa. Prueba los botones Pares e Impares y prueba el combo.


1.    Sería interesante añadir un botón “Vaciar” llamado btnVaciar que vaciara el contenido del combo. Esto se haría simplemente creando un modelo vacío y asignarlo al combo. Se anima al alumno a que realice esta mejora.


CONCLUSIÓN

Un combo, al igual que los cuadros de lista, es un objeto que contiene a su vez otro objeto denominado “modelo”.

El objeto “modelo” es el que realmente contiene los datos del combo.

Combo à Modelo à Datos

Se puede crear un “modelo” y luego introducir datos en él. Luego se puede asociar ese “modelo” al combo. De esta manera se puede cambiar el contenido del combo en cualquier momento.

Puedes descargar por dropbox Aquí




EJERCICIO JAVA: CUADROS DE LISTA

1. Realiza un nuevo proyecto.
2. En la ventana principal debes añadir lo siguiente:
a. Un botón “Aceptar” llamado btnAceptar.
b. Una etiqueta con borde llamada etiResultado.
3. Añade un cuadro de lista. Los cuadros de listas son objetos JList.

4. Cámbiale el nombre al JList. Ten cuidado, ya que en los JList aparecen siempre dentro de otro objeto llamado jScrollPane. Si miras en el Inspector, verás que al pulsar en el botón + del jScrollPane aparecerá tu JList:



5. Aprovecha para cambiarle el nombre al JList. El nuevo nombre será lstColores.
6. Si te fijas en el JList, consiste en un cuadro que contiene una serie de Items. Estos elementos pueden ser cambiados a través de la propiedad Model del JList.
7. Busca la propiedad Model y haz clic en el botón de los tres puntos. Aparecerá un cuadro de diálogo parecido al siguiente. Solo tienes que seleccionar los elementos que quieras y pulsar el botón “Borrar” (Remove) para eliminarlos de la lista.
8. Puedes añadir elementos escribiéndolos en el cuadro Artículo y luego pulsando el botón “Añadir” (Add).


9. Debes hacer que la lista sea la siguiente:
Rojo
Verde
Azul
10. Ahora programaremos el actionPerformed del botón Aceptar. Debes introducir el siguiente código:

String mensaje;
mensaje="El color seleccionado es: "+lstColores.getSelectedValue().toString();
etiResultado.setText(mensaje);

11. Observa el código:
a. Se crea una variable de cadena llamada mensaje.
b. Y dentro de esta variable se introduce una concatenación de cadenas.
c. Observa la parte:lstColores.getSelectedValue() esta parte devuelve el valor seleccionado de la lista.
d. Hay que tener en cuenta que este valor no es una cadena, por eso hay que convertirla a cadena añadiendo .toString().
e. De esta manera puedes extraer el elemento seleccionado de un cuadro de lista.
f. Luego simplemente ponemos la cadena mensaje dentro de la etiqueta.

12. Ejecuta el programa y observa su funcionamiento. Por ejemplo, si seleccionas el color verde y pulsas aceptar el resultado será el siguiente:

13. Vamos a mejorar el programa. Puede suceder que el usuario no seleccione ningún valor del cuadro de lista, y sería interesante en este caso que el programa avisara de ello. Cambie el código del botón Aceptar por este otro código:

String mensaje;
if (lstColores.getSelectedIndex()==-1) {
   mensaje="No hay un color seleccionado.";
} else {
   mensaje="El color seleccionado es: "+lstColores.getSelectedValue().toString();
}

etiResultado.setText(mensaje);

14. Observa el código:
a. El método getSelectedIndex me dice el índice del elemento que está seleccionado.
b. Por ejemplo, si está seleccionado el primero el índice es 0, si está seleccionado el segundo el índice es 1, etc.
c. Si este método devuelve -1, entonces es señal de que no hay ningún elemento seleccionado.
d. Aprovecho esto para mostrar un mensaje indicando lo sucedido.

15. Si ejecuta el programa y pulsa el botón Aceptar sin seleccionar nada el resultado debería ser el siguiente:


16. Se podría haber prescindido del botón aceptar si el código anterior se hubiera puesto en el evento mouseClicked del cuadro de lista en vez de en el actionPerformed del botón Aceptar. En este caso, cada vez que se seleccionara un elemento de la lista, automáticamente aparecería el mensaje en la etiqueta.
Se anima a que realice esta modificación.
CONCLUSIÓN
El objeto JList permite crear cuadros de lista. Estos objetos contienen una serie de elementos que pueden ser seleccionados.
A través del método getSelectedValue se puede obtener el elemento que está seleccionado. (Recuerda convertirlo a cadena con toString)
A través del método getSelectedIndex se puede saber la posición del elemento seleccionado. Si este índice es -1, entonces sabremos que no hay ningún elemento seleccionado.

Puedes descargar por dropbox Aquí



Web Services y Ejecución en Segundo plano

“Desarrollo de Aplicaciones Móviles en Android”
Ejercicio Avanzado B: Web Services y Ejecución en Segundo plano
Aunque ya hemos visto muchas cosas a lo largo del curso, aún queda
mucho por descubrir. Uno de los grandes usos que se les da a los smartphones, es el de usuarios de servicios web, o Web Services. Esto se debe a que consiguen dar una nueva vuelta de tuerca a
los servicios que ya existen (Google Search, Google Maps, Facebook, Twiiter, Flickr, Picasa, Quora, etc.) En este ejercicio, veremos una breve introducción sobre cómo acceder a ellos desde la plataforma Android.

A. Descripción
El gran auge de la denominada “Web 2.0” está fundada sobre la existencia de los Web Services. En esencia, un Web Service es un método de comunicación entre dos dispositivos electrónicos sobre una red. Para nosotros, desarrolladores de smartphones, significa que hay una serie de máquinas conectadas a Internet a las cuales podemos realizar peticiones,
peticiones que volverán a nosotros en forma de respuestas.
Todo esto se basa en la existencia de una serie de protocolos y estándares (HTTP, WSDL, SOAP,
REST, RPC, XML, JSON, etc.) además de una gran variedad de tecnologías (Apache, .NET) ¿Pero, qué es lo que importa aquí? Primero, debemos entender que hay varias formas de comunicarse con un servidor, dependiendo del protocolo que utilice éste para comunicarse
con el exterior, y lo segundo es que las comunicaciones con servicios web están basadas en APIs. Cada proveedor de servicio web tiene su propia API, y debido a que nosotros, los desarrolladores, somos usuarios de esas APIs, debemos cumplir con ellas, y estar atentos a
posibles cambios por parte de los autores, porque si no, lo pagarán nuestros usuarios.
Nosotros, en este ejercicio, llamaremos a la API de codificación geográfica de Google. ¿Nuestro
objetivo? Pasarle una posición GPS (Latitud, Longitud) y que Google nos diga cómo se llama la
calle que se encuentra en esas coordenadas.
A.1. REST
Al igual que SOA, REST es una arquitectura sobre la que pueden basarse los Web
Services. En esencia, REST implica que el servidor no posee estado, es decir, que cada petición
que hacemos al servidor es independiente de todas las anteriores y de todas las posteriores;
no hay tokens ni cookies de por medio. Esto es común en servicios que no requieren inicio de sesión, como nuestro caso, pero por ejemplo, si quisiéramos comunicarnos con Facebook para actualizar nuestro perfil, el funcionamiento sería diferente (tendríamos que mantener el inicio
de sesión como un token en este caso)

REST, además, resulta ser muy simple, pues posee un número fijo de operaciones definidas
(GET, POST, PUT y DELETE), con un propósito bien definido por el protocolo. GET se utiliza para obtener algún dato, POST y PUT para subir, modificar o añadir algo al servidor, mientras que DELETE permite eliminar un dato del servidor. En la práctica sin embargo, GET y POST son
ambos utilizados para obtener datos. La razón es muy simple; si alguna vez os habéis fijado en
las direcciones de vuestro navegador, habréis visto direcciones en las que al final hay parejas (clave, valor) separadas por un ampersand (‘&’); estas parejas son los parámetros que se le pasan al servidor, y si por ejemplo necesitamos enviar datos privados (por ejemplo: nuestra dirección), no querríamos que éstos navegaran abiertamente por la red. Por esta razón se utiliza el POST como alternativa al GET, ya que el POST permite enviar los parámetros como parte de su cabecera (dentro del paquete), lo cual permite esconder los datos un poco, e incluso deja abierta la posibilidad de encriptar los datos y enviarlos.
En la práctica, las operaciones GET, POST, PUT y DELETE son muy comunes, y no tienen por qué ceñirse a únicamente a los servicios de tipo REST; pueden ser utilizados por cualquier Web Service para que la API sea más fácil de entender, y para mantener un cierto estándar.
B. Implementación
Para comunicarnos con la API de Google necesitaremos realizar una petición de tipo GET. En concreto, el servicio que vamos a utilizar es el de reverse---geocoding (codificación geográfica inversa), el cual nos permite obtener una dirección a partir de unas coordenadas
GPS. Para ello, nos bastará con una única Activity, un layout, y varios String.
Empecemos.
Nota: Para más información acerca de la API que estamos utilizando, visitar http://code.google.com/intl/es---ES/apis/maps/documentation/geocoding/
C. Pasos a seguir
1. Comenzamos creando un nuevo proyecto Android para la versión 2.2 (NOTA: Este ejercicio sigue siendo perfectamente válido para versiones anteriores), con el
nombre que queramos, el paquete que queramos y asignándole un nombre a la
Activity por defecto.
2. Antes de escribir el layout, necesitaremos definir una serie de etiquetas
(String):
<string name="latitude">Latitud: </string>
<string name="defaultLatitude">28.482566</string>
<string name="longitude">Longitud: </string>
<string name="defaultLongitude">-16.323004</string>
<string name="result">Resultado: </string>

<string name="getAddress">Obtener Dirección</string>
3. Para el layout, utilizaremos un RelativeLayout como padre, que comenzará con dos parejas de combinaciones TextView/EditText (dos pares de TextView seguidos de EditText). Los llamaremos latitudeTV, latitudeET, longitudeTV y longitudeET respectivamente. Todos
ocuparán la mínima altura posible pero todo el ancho posible. Además, daremos a cada uno un valor a su atributo text en el orden en el que se encuentran (latitude y longitude para los TextView, defaultLatitude y defaultLongitude para los EditText). El primer TextView
(latitudeTV) ha de ir anclado a la parte superior de su padre
(android:layout_alignParentTop="true"), y el resto se irá colocando
en orden justo debajo (utilizar android:layout_below)
A continuación colocaremos el Botón para accionar la petición; se llamará requestButton, irá en la parte inferior del layout (android:layout_alignParentBottom="true"), y ocupará el mismo espacio que las View anteriores. Su texto será la etiqueta getAddress.
Para terminar con el layout, colocaremos otra pareja TextView/EditText
más: el TextView debe ir bajo el EditText longitudeET, mientras que el EditText irá bajo el TextView que acabamos de añadir y sobre el requestButton. El TextView ocupará el mismo espacio que los demás (altura mínima, pero todo el ancho posible), mientras que el EditText ocupará todo el espacio disponible, tanto en ancho como en alto. Además, nos interesa
prepararlo para que soporte largas entradas de texto (ver atributos gravity, inputType y scrollbars) Sus nombres serán resultTV y resultET respectivamente. El texto de resultTV debe ser el de la etiqueta result. Por último, este EditText no lo utilizaremos para introducir datos, sino para
mostrarlos, luego sería conveniente que el usuario no pudiese modificarlo
(android:enabled="false")
4. Una vez hayamos comprobado que el layout carga correctamente, empezaremos a escribir código en nuestra Activity. Nuestro objetivo es utilizar los dos primeros EditText para introducir las coordenadas GPS, el botón para realizar la petición, y el último EditText para mostrar la salida. Por tanto, necesitamos variables que nos permitan referenciar estas View; necesitaremos una referencia a latitudeET, un longitudeET, un requestButton y un resultET, todos son sus tipos correspondientes (llamar las variables de la misma forma). Utilizaremos nuestro método initConfig() para cargar el layout y referenciar
las View. A continuación, añadiremos un onClickListener al
requestButton y haremos que el anterior llame al método makeRequest()
sin argumentos. En este método implementaremos la petición.
5. Escribimos la cabecera del método privado makeRequest() sin argumentos y sin ningún valor a devolver. Lo primero que debemos hacer en él es desactivar los EditText y el Button para que el usuario no pueda realizar modificaciones ni llamar a este método más de una vez al mismo tiempo. Al final del método, escribimos el código contrario, es decir, el que vuelve a activar los EditText y el
requestButton.
6. El código importante viene aquí. En medio del método makeRequest() ( tras desactivar los EditText y el requestButton ) debemos añadir un bloque try/catch genérico (que capture cualquier Exception) Dentro del bloque try/catch, introduciremos el siguiente código:

// obtenemos una conexión
DefaultHttpClient httpClient = new DefaultHttpClient(); String url =
"," + longitude + "&sensor=false";
// creamos una petición de tipo GET (parámetros en la URL) HttpGet request = new HttpGet();
request.setURI(new URI(url));
// ejecutamos la petición
HttpResponse response = httpClient.execute(request);

// convertimos la respuesta en string
String responseString = EntityUtils.toString(response.getEntity());

// escribimos el resultado resultET.setText(responseString);


// cerramos la conexión httpClient.getConnectionManager().shutdown();

7. Con este código en posición la aplicación debería funcionar correctamente. ¿O no?
¿Nos hemos olvidado de algo? Sí. Queremos realizar una petición en Internet,
luego nos hace falta el permiso (uses-permission) INTERNET en el manifest
de nuestra aplicación.
8. Como podemos ver, la aplicación retorna un resultado muy largo, dentro del cual
se encuentra la calle de la Escuela Técnica Superior de Ingeniería Informática de
la Universidad de La Laguna, pero, ¿qué es todo este texto que recibimos como respuesta? Y lo que es más importante aún, ¿por qué se detiene la aplicación mientras realiza la petición?
B (2) Ejecución en segundo plano
Detengámonos un segundo a mirar lo que acabamos de hacer. Android posee varias
librerías incluidas, y una de ellas (HttpClient), es la que hemos utilizado para obtener
acceso a Internet. Puede verse que primero necesitamos inicializar un objeto de tipo HttpClient y que luego, al final de la petición lo apagamos ( método shutdown() ). En este ejercicio hemos querido simplificar el código lo máximo posible para centrarnos en lo importante: entender cómo implementar comunicaciones con Web Services, pero en realidad,
el hecho de inicializar y de cerrar clientes HTTP es una operación costosa, y en aplicaciones que requieran de acceso a Internet a lo largo de muchas Activity lo recomendado es compartir
un único HttpClient compartido, y que éste se cierre únicamente cuando el usuario ya no
esté utilizando la aplicación.
Lo siguiente a tener en cuenta es la respuesta. Como ya hemos mencionado, nuestro Web Service en cuestión posee su propio protocolo, uno al que ya nos hemos adaptado (en parte) al realizar la petición (nótese que los parámetros están en la URL), sin embargo, la sorpresa viene al ver que la respuesta no nos da los datos de forma directa, ni mucho menos.
La respuesta que tenemos es en realidad un objeto JSON (JavaScript Object Notation), al que muchos nos gusta llamar “el sucesor del XML”. Lo común cuando se interactúa con Web Services es recibir la respuesta o bien en formato XML, o bien en formato JSON. XML tiene la ventaja de ser muy conocido y de que es increíblemente flexible, además de que está soportado por casi todos los lenguajes. Sin embargo, tiene una gran desventaja con respecto al
JSON, y es que requiere de la construcción de un parser, un analizador o escáner, específico para cada tipo de documento XML. Esto significa que para un mismo Web Service, podríamos tener que escribir dos o más escáneres, uno por cada petición distinta que tengamos que hacer (en la práctica, los Web Service intentan minimizar que esto ocurra) Es aquí donde gana
el más moderno JSON: funciona por parejas (clave, valor), se adapta a cualquier estructura,
cada vez es más soportado, y además, no requiere construir un parser específico, solamente
adaptarnos a la respuesta recibida.
Lo más importante, sin embargo, es el hecho de que nuestra aplicación se detiene cada vez que realiza la petición. Con detenerse queremos decir que, si nosotros interactuamos con la aplicación mientras está realizando la llamada al Web Service, ésta no es capaz de hacernos caso. ¿Por qué? La respuesta es muy simple: porque está ocupada.
Hasta ahora, todas las acciones que hemos realizado en Android han sido acciones
rápidas, sin apenas retardo ni complejidad; esto ha permitido que pasemos por alto un hecho
muy importante: hemos estado utilizando el hilo (thread) encargado de procesar los eventos
de la interfaz gráfica para todas nuestras acciones.
Es decir, la secuencia de instrucciones que mantiene viva nuestra aplicación, respondiendo cada vez que nosotros presionamos la pantalla, pulsamos un botón, o simplemente deslizamos un dedo para ver cómo se ilumina una parte de la pantalla, es la misma que ejecuta todas y cada una de las líneas de código que hemos programado hasta
ahora (eventos, creación de múltiples Activity, la rotación, etc.) han detenido brevemente
la capacidad de la aplicación de responder ante nuestras peticiones, pero debido a que le
hemos pedido relativamente poco al hilo de la interfaz gráfica, no nos hemos dado cuenta.
Todo ha cambiado con la petición al Web Service, donde dependiendo del dispositivo y
de la red no nos damos cuenta, pero el hecho es que si una interacción por parte del usuario tarda más de medio segundo, el usuario medio se da cuenta, y a los cinco segundos, Android permite al usuario cerrar la aplicación.
Esto se soluciona moviendo las partes de código lentas a otro hilo, uno secundario, que pueda realizar estas acciones en paralelo. Mientras, el hilo de la interfaz mantendrá la aplicación “viva”, y cuando el hilo secundario termine, podrá notificar al primer hilo para que
actualice la interfaz. Hagámoslo.
D. Pasos a seguir
1. Debemos crear un nuevo paquete, esta vez para guardar las clases de tipo Thread. Una vez creado, pediremos a Eclipse que añada una nueva clase, RequestThread, que heredará de la clase java.lang.Thread.
2. La clase RequestThread estará vacía. Los datos que necesitamos son la latitud, la longitud y un Handler. Un Handler es un objeto de Java capaz de recibir mensajes (eventos) de forma asíncrona, es decir, sin detener el hilo de ejecución
desde el que fue llamado. Además, funciona muy bien cuando se le llama desde un
hilo distinto. Comenzamos añadiendo estas líneas de código a la cabecera de la
clase RequestThread:

private Handler myHandler;
private String latitude;
private String longitude;

3. A continuación, añadimos un constructor a la clase RequestThread para poder inicializar las variables anteriores. Como siempre, lo primero que hacemos es llamar al constructor del padre (constructor de la clase Thread de Java):

public RequestThread(String latitude, String longitude, Handler myHandler) {
super();
this.latitude = latitude; this.longitude = longitude; this.myHandler = myHandler;
}
4. Lo siguiente es añadir el método más importante, el método run() (público, sin retorno y con uso del @Override porque estamos sustituyendo un método de la clase padre). Éste es el método del hilo que se ejecuta en segundo plano, ya que
por lo demás, cuando vayamos a crear un objeto de tipo RequestThread desde la RequestActivity, funcionará sobre el mismo hilo, es decir, sobre el de la interfaz gráfica. Copiar el siguiente código al método run():
try {

// obtenemos una conexión
DefaultHttpClient httpClient = new DefaultHttpClient();

String url = "http://maps.googleapis.com/maps/api/geocode/json?latlng=" + latitude +
"," + longitude + "&sensor=false";

// creamos una petición de tipo GET (parámetros en la URL) HttpGet request = new HttpGet();
request.setURI(new URI(url));
// ejecutamos la petición
HttpResponse response = httpClient.execute(request);

// convertimos la respuesta en string
String responseString = EntityUtils.toString(response.getEntity());

// escribimos el resultado
Message msg = new Message();
// de qué trata el mensaje msg.what = 0;
// añadir datos
Bundle data = new Bundle();
data.putString(RequestActivity.__RESPONSE_KEY__, responseString);
// colocamos los datos
msg.setData(data);
// enviamos el mensaje myHandler.sendMessage(msg);

// cerramos la conexión httpClient.getConnectionManager().shutdown();
}
catch (Exception e) {
e.printStackTrace();
// mensaje vacío (sin datos)
myHandler.sendEmptyMessage(1);

}
Revisemos el código con calma. Lo primero es ver que se trata del mismo bloque try/catch que nos detenía la ejecución de la aplicación en el método makeRequest() de la RequestActivity. Los cambios vienen a partir de que obtenemos la respuesta como String, pero antes miremos la nueva línea del bloque catch. En ella, enviamos un mensaje al Handler, un mensaje vacío (sin
datos), en el que el único parámetro que hay asigna un valor al campo what del
Message. Este campo what se utiliza para identificar qué se está enviando con el
mensaje; cuando se quieren enviar distintos mensajes sin enviar datos pesados, se envían mensajes vacíos con distintos valores para el campo what. El receptor, que pondremos en la clase RequestActivity, preguntará al mensaje cuál es el valor de su campo what, y así sabrá qué significa la recepción de ese mensaje. Si miramos el final del bloque try, veremos que creamos una nueva variable de tipo
Message, que asignamos el valor del campo what a cero (en el catch le asignamos 1), que creamos un bundle, y que en el bundle añadimos el String que hemos recibido (como clave utilizamos un campo que añadiremos ahora a la clase RequestActivity). Por último, colocamos el bundle en el
mensaje, y lo enviamos, sin olvidarnos de cerrar la conexión.
5. Como acabamos de mencionar, debemos añadir una variable pública, estática,
final (final) de tipo String con nombre RESPONSE_KEY
a la cabecera
de la clase RequestActivity. Además, debemos ir al método makeRequest(), y eliminar todas las líneas que siguen a aquellas en las que desactivamos los EditText y el requestButton. A continuación, creamos un RequestThread a cuyo constructor le pasamos el texto de los EditText (latitudeET, longitudeET) y una variable (myHandler) que crearemos en
el último paso. Para terminar, llamamos al método run(), sin parámetros, del
RequestThread que acabamos de crear.
6. Tras el método makeRequest(), debemos añadir el siguiente código:

Handler myHandler = new Handler() {

public void handleMessage(Message msg) {
if (msg.what == 0) {
// response resultET.setText((String)msg.getData().get(Request
Activity.__RESPONSE_KEY ));
}
if (msg.what == 1) {
resultET.setText(getString(R.string.error));
}
// activamos todo latitudeET.setEnabled(true); longitudeET.setEnabled(true); requestButton.setEnabled(true);
}

};

Como podéis ver, el método es muy sencillo. Si el what es cero, recogemos el String y se lo asignamos al EditText de resultado; si es 1, es que ocurrió una excepción, luego debemos mostrar un mensaje de error (dejamos este String a los alumnos). En cualquier caso, la ejecución del hilo termina, luego podemos volver a activar los EditText y el botón de petición.
E. Cuestiones y Conclusiones Finales
1. ¿Por qué utilizamos el método start() en vez de llamar al método run()
directamente desde el makeRequest() en RequestActivity? Probarlo.
¿Por qué se comporta de esta forma?
2. ¿Por qué utilizamos un Handler? ¿Por qué no podemos realizar lo que hace el
Handler directamente desde el método run() de la clase RequestThread?
¿Qué ocurre si lo hacemos?
3. ¿Por qué tenemos tanto miedo de que la llamada al Web Service detenga nuestra
aplicación? ¿Qué consecuencias tiene esto para el usuario?

Vayamos por partes. Lo primero es que sí, son muchos conceptos para asimilar en un único ejercicio, pero ésta, en realidad, es la forma más simple y real de encontrarse con el problema de bloquear (o detener) el hilo de la interfaz gráfica. Este problema lo vivimos constantemente con las aplicaciones de escritorio que utilizamos, sin embargo, en un smartphone la situación resulta ser mucho más estresante, ya que el usuario no tiene ningún sitio al que correr si nuestra aplicación satura al sistema operativo (dependiendo del teléfono,
es posible volverlo tan lento que el propio proceso HOME tenga que cerrarse), al contrario que en Windows y en Mac, donde podemos cerrar las aplicaciones y volver a abrirlas si nos hace
falta. Esta estrategia de Thread(s) y Handler(s) no es exclusiva de Android, y puede
utilizarse perfectamente en Java. Sin embargo, cabe mencionar que no es la única forma de realizar esta gestión en Android; hay dos tipos más que esperamos ver en un futuro curso Avanzado de Android.
Lo siguiente es reforzar la importancia de los Message. Cuando se envía un mensaje a un Handler, éste es colocado al final de una cola para ser procesado por el método handleMessage() que nosotros sustituimos en nuestro Handler. Esto ocurre de forma asíncrona, provocando que el hilo en el que se encuentra el handler ejecute un
handleMessage() con el mensaje correspondiente. Esto nos permite utilizar esta
estrategia con mucha flexibilidad; podemos crear tantos eventos (valores del argumento what) como queramos, permitiéndonos conocer en cualquier momento en qué estado se encuentra nuestro hilo en segundo plano. Naturalmente, la utilidad de esta estrategia se ve
mejor cuanto más complicada es la interacción entre el hilo en segundo plano y el hilo de la interfaz gráfica. Podría incluso utilizarse un único handler para recibir eventos de múltiples hilos, pues todos van a la misma cola. Ahora bien, las acciones que llevemos a cabo en el
handler no pueden ser lentas (es decir, no pueden detener el hilo de la interfaz gráfica), o
estaremos de vuelta en el mismo punto del que partimos.
Ahora vienen los hilos. El hilo de tipo RequestThread que creamos desde la
RequestActivity, en realidad, se ejecuta desde el mismo hilo que lo creó (es decir, el de
la interfaz gráfica) Si nosotros llamásemos al método run() directamente, sería como si el
hilo no existiese, porque sería como si ejecutásemos un método más de un objeto cualquiera;
es decir, se ejecutaría desde el hilo de la interfaz, luego el esfuerzo que hemos hecho habrá
sido en vano.
En realidad, las clases Thread y similares (Runnable) lo que hacen es
proporcionarnos una forma fácil de ejecutar código en otro hilo sin que nosotros tengamos que realizar ninguna gestión. Por eso la utilidad de la clase Thread radica en que utilicemos el método start(), porque se encarga de crear un nuevo hilo y de mantenerlo en funcionamiento ejecutando código, y el código que ejecuta es, por supuesto, el del método run().
Nota: La clase Thread proporciona más métodos para controlar la ejecución del hilo que ejecuta el método run(). Se han obviado para simplificar el texto y transmitir las ideas principales.
Sin embargo, ejecutar código en segundo plano es un poco más complicado de lo que
hemos dicho hasta ahora. Si nosotros intentáramos llamar a las Views desde el método
run() del hilo, obtendríamos una excepción en tiempo de ejecución, una excepción medianamente grave. Esto se debe a que las Views no pertenecen al hilo que está ejecutando el código (RequestThread), sino a otro (hilo de interfaz de RequestActivity) Es una convención en frameworks de programación con interfaz gráfica (.NET, Cocoa, Cocoa Touch, y por supuesto Android) que los elementos de la interfaz sólo puedan ser llamados desde el propio hilo que los creó. Esto da lugar a un concepto muy
utilizado, el de thread safety. Cuando un elemento es thread safe, significa que puede ser utilizado (llamado) desde cualquier hilo. En el caso de Android los elementos de la interfaz (Views) no son thread safe. Es aquí donde el handler comienza a tener sentido; los
métodos que permiten enviarle mensajes al handler son thread safe, y como ya dijimos, el
método que procesa los mensajes se ejecuta desde el hilo que creó el handler (en nuestro
caso, el de la interfaz de RequestActivity) Así de cómo hemos decidido solucionar el problema de comunicarnos desde el código ejecutando en segundo plano con la interfaz en este ejercicio, pero como ya hemos dicho, hay más soluciones.
Existen otros métodos para realizar comunicaciones con un servidor; Android, al implementar un subconjunto de Java 5, suele ser muy flexible importando librerías hechas para Java (archivos .jar), lo que nos permite utilizar otra librería con la que nos sintamos
más cómodos o incluso aportar algo que no permitía la librería que incluye Android, como por
ejemplo, el envío de archivos mediante un POST (recomendamos utilizar la librería Apache
Commons para esto) Sin embargo, llegados a cierto punto, simplemente conocer cómo
funciona la plataforma o librería con la que estemos trabajando no nos llevará más lejos, sino que será nuestro ingenio el que tendrá que resolver los problemas que no han sido resueltos.