miércoles, 12 de marzo de 2008

Leer y escribir archivos en J2ME

En muchos casos se necesita leer y escribir archivos a las unidades de almacenamiento del teléfono. Uno de los escenarios posibles es la exportación e importación de datos.

En esta nota voy a mostrar breves ejemplos de como realizar estas tareas utilizando la API FileConnection.

La clase FileConnection forma parte de los paquetes opcionales a J2ME. Por ese motivo al crear un nuevo proyecto en Sun Wireless Toolkit, al momento de elegir la API, debemos marcar:
Target Platform = JTWI y tildar PDA Profile for J2ME (JSR 75)


Hay que destacar que al ser una especificación optativa puede no estar presente en todos los teléfonos o dispositivos. En general todos los teléfonos actuales lo soportan, pero no es así para los modelos viejos.

En el emulador el sistema de archivos "virtual" se encuentra en la carpeta "..j2mewtk\2.5.2\appdb\DefaultColorPhone\filesystem\".

Dentro de esta carpeta podemos encontrar otra llamada root1. Cada carpeta en "filesystem" es tomada como una unidad o root por el emulador. En el télefono cada root corresponderá a un dispositivo de almacenamiento diferente, en la mayoría de los casos uno será la memoria interna del teléfono y otro la tarjeta de memoria.

El primer ejemplo trata de como listar todos los roots disponibles. Esto se logra utilizando la clase FileSystemRegistry:

public void SelectRoot() {
Enumeration rootListEnum = FileSystemRegistry.listRoots();
listRoots = new List("Elija el root:", Choice.EXCLUSIVE);
while (rootListEnum.hasMoreElements()) {
String rootName = (String) rootListEnum.nextElement();
listRoots.append(rootName, null);
}
...


Este fragmento utiliza la función listRoots() que devuelve una lista de los roots y luego la carga en un control de lista para mostrarla en la pantalla.

Para listar el contenido de un directorio:

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();



Las tres primeras líneas son obligatorias cuando estemos utilizando un CommandListener por lo expuesto en Acceso a archivos y threads, su finalidad es evitar que la aplicación se cuelgue si hay un problema en las operaciones de manejo de archivos.

En lo demás este caso es similar al de listado de roots, utilizando el método "list".

Finalmente para escribir un archivo debemos utlizar además un Data Stream:


Thread t = new Thread() {
public void run() {
try{

FileConnection conn = (FileConnection) Connector.open(writeFile, Connector.READ_WRITE);
if (!conn.exists()) {
conn.create();
}
OutputStream outSt = conn.openOutputStream();
PrintStream printSt = new PrintStream(outSt);
printSt.println(writeContents);
conn.close();

} catch(IOException e) {
System.out.println("IOException in WriteToFile: " + e.toString());
} catch(SecurityException e) {
System.out.println("SecurityException in WriteToFile: " + e.toString());
}
}
};
t.start();


La referencia de este paquete opcional se encuentra en:
JSR-000075 PDA Optional Packages for the J2METM Platform. Ahí puede descargarse las especificaciones de FileConnection con todas sus clases y métodos.
Y una serie de artículos sobre esto en:
Mobile Service Architecture - JSR 75



El código completo de esta clase ejemplo es:
import javax.microedition.midlet.MIDlet;
import javax.microedition.io.file.*;
import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;

public class FileDemo extends MIDlet implements CommandListener {
List listRoots, listContents;
private String currentRoot, currentPath, writeFile, writeContents;
private Command cmOK,cmExit, cmNew;
private Form WaitForm;

public void FileDemo() {
}

public void startApp() {
WaitForm = new Form("Espere por favor...");
WaitForm.setCommandListener(this);
cmExit = new Command("Exit", Command.EXIT, 1);
cmNew = new Command("New", Command.ITEM,2);
SelectRoot();
}

public void pauseApp() {
}

public void destroyApp(boolean condition) {
notifyDestroyed();
}

public void SelectRoot() {
Enumeration rootListEnum = FileSystemRegistry.listRoots();
listRoots = new List("Elija el root:", Choice.EXCLUSIVE);
while (rootListEnum.hasMoreElements()) {
String rootName = (String) rootListEnum.nextElement();
listRoots.append(rootName, null);
}
cmOK = new Command("OK", Command.ITEM, 1);

listRoots.addCommand(cmOK);
listRoots.addCommand(cmExit);
listRoots.setCommandListener(this);
Display.getDisplay(this).setCurrent(listRoots);


}

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.addCommand(cmNew);
listContents.setCommandListener(this);
Display.getDisplay(this).setCurrent(listContents);

}

public void WriteToFile(String fileName, String contents) {
writeFile = fileName;
writeContents = contents;

Thread t = new Thread() {
public void run() {
try{

FileConnection conn = (FileConnection) Connector.open(writeFile, Connector.READ_WRITE);
if (!conn.exists()) {
conn.create();
}
OutputStream outSt = conn.openOutputStream();
PrintStream printSt = new PrintStream(outSt);
printSt.println(writeContents);
conn.close();

} catch(IOException e) {
System.out.println("IOException in WriteToFile: " + e.toString());
} catch(SecurityException e) {
System.out.println("SecurityException in WriteToFile: " + e.toString());
}
}
};
t.start();
}

public void commandAction(Command cm, Displayable d) {
//System.out.println("En commandAction");
if (cm == cmOK && d == listRoots) {
System.out.println("Elegiste: " + listRoots.getString(listRoots.getSelectedIndex()));
currentRoot = listRoots.getString(listRoots.getSelectedIndex());
ShowContents();
} else if (cm == cmExit) {
destroyApp(false);
} else if (cm == cmNew) {
WriteToFile("file:///" + currentRoot + "newfile.txt","Sample, sample, sample");
}
}
}

16 comentarios:

Anónimo dijo...

Hola, gracias por tucódigo. Tengo un problema, cuando lo cargo en mi movil y selecciono con la unidad que quiero trabajar, ya sea C o E, me da el mensaje "Acceso de aplicación establecido en no permitido", como lo soluciono? es una config del móvil o es con código? Gracias. (Uso un nokia 5200, y Netbeans).

Guish dijo...

Por el mensaje creo que es una configuración general de tu teléfono. No conozco ese modelo en particular, pero deberías buscar en opciones de seguridad o algo así.

Anónimo dijo...

A mi me ocurre el mismo problema. Tengo un nokia 6085, y cuando ejecuto alguna de estas aplicaciones que usan el api JSR 75, me da error de Seguridad "Acceso denegado".
Ya he probado con varias aplicaciones incluso, las que vienen en la pagina nokia. No puedo write text in File.
En cual dispositivo has probado usa esta API?

Guish dijo...

Yo lo probé en un Sony Ericsson W580. Parece ser que los Nokia son mas restrictivos en cuanto a permisos. Probablemente no se pueda escribir directamente en el root "C" o "E". Podrías intentar grabar en una subcarpeta.
Les dejo este link sobre un ejemplo del uso de la API JSR-75 en Nokia:
http://www.forum.nokia.com/info/sw.nokia.com/id/de0f933c-0bd3-4143-b62a-ab867a43409a/MIDP_FileConnection_API_Developers_Guide_v2_0_en.zip.html

Unknown dijo...

No sabrias como se configuran puertos bluetooth para sincronizar la pc y el celular?
En la web hay muchos codigos para emular la trasmicion de datos. Pero todos funcionan desde el emulador.
Tambien encontre en la pagina de Nokia, para sincronizar J2ME Y J2SE(usa la lib. Bluecove.jar).
La aplicacion J2ME(que hace de Servidor) esta en un loop en espera de una conexión.
Al ejecutar la aplicación de escritorio encuentra al Celular pero cuando quiere conectarse, me sale este error: "No estan usando el mismo puerto".
No se si es problema de configuración del bluetsoil o del codigo java.

Unknown dijo...

No sabrias como se configuran puertos bluetooth para sincronizar la pc y el celular?
En la web hay muchos codigos para emular la trasmicion de datos. Pero todos funcionan desde el emulador.
Tambien encontre en la pagina de Nokia, para sincronizar J2ME Y J2SE(usa la lib. Bluecove.jar).
La aplicacion J2ME(que hace de Servidor) esta en un loop en espera de una conexión.
Al ejecutar la aplicación de escritorio encuentra al Celular pero cuando quiere conectarse, me sale este error: "No estan usando el mismo puerto".
No se como se si es problema de configuración del bluetsoil o de codigo;

Guish dijo...

No probé conectar la PC con el móvil. Si escribí alguna aplicación bluetooth entre celulares. No puedo decirte mucho sobre tu problema porque no vi el código.

Básicamente la definición de una conexión depende de la UUID y el nombre del servicio. La UUID es algo muy parecido al puerto, así es que te diría que chequees si el servidor y el cliente están usando la misma.

Suerte.

Anónimo dijo...

Muy Bueno!, anda y todo je, como sería para agregarle texto en nuevas líneas para no borrar la anterior? Gracias.

Hugo Pallaoro dijo...

Tanto para el ejemmplo FileDemo, como para una aplicación que hice basándome en este código, tengo el mismo problema. Me crea el archivo, pero no lo escribe, lo estoy corriendo sobre Nokias S60, no tengo otra marca para probar. A Alguien le pasó? Sospecho que sea algún permiso de los nokia.
Saludos

Unknown dijo...

No tiene nada que ver con los Nokia. Falta esto:
outSt.close();
antes de cerrar la conexión.

Unknown dijo...

hola amigo, ayúdame por favor, estoy haciendo una aplicación móvil que mediante RMS almacena varios datos que ingresa el usuario, pero quiero sincronizar esos datos con el PC, únicamante con conectar el celular al PC mediante el cabe USB. La idea es q en en la PC se abra un bloc de notas con los datos guardados de la aplicacion y no debe usar ningún programa extra para instalar o ejecutar en el PC ya q las personas que van a usar la aplicacion no tienen conocimientos de computadores. Cómo hago? ayúdame. Te lo agradecería mucho si me ayudas.

Unknown dijo...

Además, la aplicacion la estoy haciendo en netbeans 6.7 y está diseñada par que funcione en cualquier celular que disponga de symbian, java y midp2.0, aunq estoy trabajando con el nokia 6120 classic. Gracias por cualquier ayuda

Rodolfo Muriel dijo...

Oye, me puedes decir, por favor, si existe forma de que los datos que guardas en el archivo que creas en tu código, se pueden pasar a una pc. Lo que necesito es crear archivos en el celular, escribir datos en esos archivos usando el celular y, por ejemplo, tu código y posteriormente pasarlos (estrictamente pasar el archivo que creé en el celular)a la pc, para leerlos, por ejemplo, mediante algún porograma que yo cree en j2se (no en el j2me), bueno del programa en j2se me encargo yo, pero lo que necesito saber es lo que te digo . Muchas Gracias por tu atención!!!!

Anónimo dijo...

hola!!

Mis felicitaciones el blog es buenisimo... Esperaba q puedas ayudarme xq copie parte de tu codigo para escribir un txt... el el emulador funciona en el celular no pasa nada... help me

Ah nose como poner eso de la API

saludos

Fabián dijo...

chido men, gracias, funciona muy bien en BlackBerry

Anónimo dijo...

Hola muchachos estoy trabajando en una aplicación para celular en J2ME y lo que tiene que hacer es leer un archivo que este almacenado en el celu, he leido que se puede hacer con fileconnection osea utilizando el API PDA para J2ME, pero resulta que el celular no posee esa API, quisiera saber si hay alguna otra forma de leer archivos con una aplicación J2ME