martes, 11 de marzo de 2008

Acceso a archivos y threads

Corriendo un ejemplo de escritura de archivos en Sun Java Wireless Toolkit sucedía que la aplicación se colgaba al momento de abrir el archivo.

En la consola se mostraba este mensaje:
Warning: To avoid potential deadlock, operations that may block, such as
networking, should be performed in a different thread than the
commandAction() handler.


El mismo problema aparecía en el ejemplo de lectura de un directorio que estaba escribiendo. En ambos casos la aplicación se colgaba al llamar a Connector.open.

La advertencia en cuestión quedaría traducida a:
"Para evitar posibles puntos muertos, las operaciones que pueden bloquear, como conexiones de red, deben ser ejecutadas en un hilo (thread) diferente que el administrador de commandAction()"

El "commandAction() handler" sería la clase que especificamos para manejar la interacción con el usuario mediante el método setCommandListener.

Como un conexión de red o un acceso a un archivo puede dejar inutilizable la interfaz de usuario, debe correrse en un thread separado.

Una muy buena explicación de esto puede encontrarse en:
Networking, User Experience, and Threads

En cuanto a la solución de este error en particular fue cambiar este código:


public void ShowContents() {
listContents = new List("Contenidos de " + currentRoot, Choice.EXCLUSIVE);

try{

FileConnection myFc = (FileConnection)
Connector.open("file:///" + currentRoot);
Enumeration fileEnum = myFc.list("*", false);
while(fileEnum.hasMoreElements()) {
String fileName = (String) fileEnum.nextElement();
listContents.append(fileName, null);
}
} catch (Throwable th) {
System.out.println(th.toString());
}

listContents.addCommand(cmExit);
listContents.setCommandListener(this);
Display.getDisplay(this).setCurrent(listContents);
}

Por este:

public void ShowContents() {
listContents = new List("Contenidos de " + currentRoot, Choice.EXCLUSIVE);
Display.getDisplay(this).setCurrent(WaitForm);
Thread t = new Thread() {
public void run() {
try{

FileConnection myFc = (FileConnection)
Connector.open("file:///" + currentRoot);
Enumeration fileEnum = myFc.list("*", false);
while(fileEnum.hasMoreElements()) {
String fileName = (String) fileEnum.nextElement();
listContents.append(fileName, null);
}
} catch (Throwable th) {
System.out.println(th.toString());
}
}
};
t.start();
listContents.addCommand(cmExit);
listContents.setCommandListener(this);
Display.getDisplay(this).setCurrent(listContents);

}

De acuerdo a lo explicado en el artículo citado.

Para ver un ejemplo de como listar roots y escribir archivos ir a:
Leer y escribir archivos en J2ME

Uso de la clase java.util.Stack en flujo de aplicaciones J2ME

Una pila (stack en inglés) es una estructura de datos de tipo LIFO (del inglés Last In First Out, último en entrar, primero en salir) que permite almacenar y recuperar datos. Se aplica en multitud de ocasiones en informática debido a su simplicidad y ordenación implícita en la propia estructura.

Para el manejo de los datos se cuenta con dos operaciones básicas: apilar (push), que coloca un objeto en la pila, y su operación inversa, retirar (o desapilar, pop), que retira el último elemento apilado. Ver Pila (estructura de datos) en Wikipedia.

Java provee una implementación de esta estructura de datos en la clase java.util.Stack que cuenta con los métodos empty, peek, pop, push y search.

Push: agrega un elemento a la pila.
Pop: recupera el último elemento y lo elimina.
Peek: recupera el último elemento sin eliminarlo de la pila.

Un ejemplo de utilización de esta clase puede ser para almacenar el flujo de una aplicación J2ME como se ve en este ejemplo adaptado del código de Cash2ME:


package net.sf.cash2me;

import java.util.Stack;

import javax.microedition.lcdui.Displayable;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class Cash2ME extends MIDlet {

private Displayable currentScreen; // La pantalla actual
private Stack screenStack; // contiene todas las pantallas previas

/**
* Recibe un objeto Displayable y lo configura como Current de Display, es decir
* lo muestra en la pantalla.
* @param screen El objeto Displayable a mostrar.
*/
private void setCurrent(Displayable screen) {
if (currentScreen == null || screen != currentScreen) {
if (screenStack == null)
screenStack = new Stack();
screenStack.push(currentScreen); //almacena la pantalla actual en la pila.
currentScreen = screen;
}
Display.getDisplay(this).setCurrent(currentScreen); //muestra la pantalla recibida
}

/**
* Muestra la pantalla anterior.
*/
public void showPrevious() {
if (screenStack != null) {
/*Controla si hay más de una pantalla en la pila*/
Displayable screen = screenStack.size() > 1
/*Si hay más de una pantalla en la pila recupera la última y la elimina de la pila.*/
? (Displayable)screenStack.pop()
/* Si queda solo una, utiliza el método peek para recuperar la última pantalla
sin borrarla de la pila.*/

: (Displayable)screenStack.peek();
if (screen != null)
Display.getDisplay(this).setCurrent(currentScreen = screen);
}
}
}


En este ejemplo en lugar de recurrir directamente al método Display.setCurrent se recurre a estos métodos para llevar un "historial" de las pantallas recorridas. De esta forma cada vez que a lo largo de la aplicación se muestra una pantalla nueva, primero se guarda la pantalla actual en la pila y luego se muestra la nueva.

Cuando la aplicación necesita volver a la pantalla anterior, recupera la última pantalla, que estará arriba de la pila, la muestra y la elimina, usando el método pop.

Si se llega al final de la pila, quedando una sola pantalla en la pila, se utiliza el método peek para recuperar dicha pantalla sin eliminarla.

Más Información
Otro ejemplo de pilas en java: How do I use stacks in Java?
Referencia de la clase Stack: Class Stack

miércoles, 30 de enero de 2008

Como llevar el diario en el celular

Una de las funciones más interesantes del W580i es que puede navegar cualquier página html, sin que sea necesariamente una especial para celulares.

Esto lo consigue adaptando la página web al tamaño de la pantalla, reordenando el texto y cambiando el tamaño de las imágenes.

El problema es que las páginas web normales tienen un "peso" mucho mayor y puede resultar en una factura de teléfono bastante pesada.

Cómo navegar offline
Para poder bajarse todo un sitio puede usarse WinHTTrack Website Copier. Luego esa carpeta copiarla al teléfono en la carpeta "Web Saved Pages".

Cómo obtener una copia del sitio más facil de navegar
Algunos sitios tienen muchos graficos y tardan mucho en cargase en el navegador. Hay varios servicios que, combinados con HTTrack, permiten llevar un copia más "liviana" de un sitio web en el celular.
www.mowser.com
www.skweezer.net
www.google.com/gwt/n
www.baresite.com

Estos sitios implementan lo que se llama Mobile Transcoding