|
|
Márcio Guedes
Tudo o que você precisa saber quando guardar dados com J2ME.
Download do material relacionado ao tutorial
Introdução
Para suportar a grande variedade de produtos referentes a J2ME, a Sun criou o conceito de Configurações (Configurations). Uma configuração define uma plataforma para uma variedade de dispositivos de pequeno porte. A idéia de uma configuração é muito ligada a uma VM. De fato, uma configuração define as caracteristicas da linguagem e as bibliotecas disponíveis.
Basicamente existem dois tipos de configurações: CDC (Connected Device Configuration) e CLDC (Connected Limited Device Configuration). Veja a seguir as caracteristicas de cada uma:
CDC
Minimo de 512 k para executar a VM;
256 k para alocacao de memória;
CLDC
Minimo de 128 k para executar a VM;
32 k para alocacao de memória;
Interface mais restrita;
Mesmo com a divisão do J2ME entre as configurações, a variedade entre dispositivos ainda é muito grande. Para resolver isso a Sun criou extensões das configurações chamadas Profiles. Um Profile define as características da linguagem para um tipo particular de dispositivo.
Este tutorial irá demonstrar como utilizar um recurso poderoso do profile MIDP para realizar persistencia de dados chamado RMS (Record Management System). Você pode utilizar qualquer editor de sua preferência. Faça o download do J2ME Wireless Toolkit no site da Sun. Para saber mais detalhes sobre isso, veja o primeiro tutorial de MIDP na seção de tutoriais do GUJ.
Entendendo RMS
MIDP possui um conjunto de classes para realizar a persistência de dados chamado RMS (Record Management System). O mecanismo de armazenamento do RMS é implementado como uma coleção de registros onde cada registro é organizado como um array de bytes. O tamanho do array de bytes pode variar para cada registro e não existem limitações para o seu conteúdo.
Um record store é representado pela classe javax.microedition.rms.RecordStore. Através do método openRecordStore é possível uma instância, veja o código a seguir:
01
02 public void open() {
03 try {
04 rs.deleteRecordStore(RS_NAME);
05 rs = RecordStore.openRecordStore(RS_NAME, true);
06 }
07 catch (RecordStoreNotFoundException e) {
08 System.out.println("-- RecordStore inexistente");
09 }
10 catch (RecordStoreException e) {
11 System.out.println("-- Outro erro");
12 }
13 }
|
O primeiro parâmetro do método openRecordStore é o nome da coleção de registros (record store). O segundo parâmetro indica que, se o record store nao existir, ele será criado. O nome do record store não pode ter mais de 32 caracteres.
A classe de exceção RecordStoreNotFoundException extende RecordStoreException que é a classe base para todas as exceções RMS. A exceção RecordStoreException é levantada se o record store não puder ser criado por falta de espaço ou algum erro interno.
Para fechar um record store execute o método closeRecordStore, veja o código a seguir:
01
02 public void close() {
03 try {
04 rs.closeRecordStore();
05 }
06 catch( RecordStoreNotOpenException e ){
07 System.out.println("-- O Record Store esta fechado");
08 }
09 catch( RecordStoreException e ){
10 System.out.println("-- Outro erro");
11 }
12 }
|
Adicionando Registros com RMS
É possível adicionar registros ao record store aberto utilizando o método addRecord, veja o código a seguir:
01 public void add(String nome) {
02 try {
03 ByteArrayOutputStream baos = new ByteArrayOutputStream();
04 DataOutputStream dos = new DataOutputStream(baos);
05 dos.writeUTF(nome);
06 dos.flush();
07 byte[] data = baos.toByteArray();
08 int id = rs.addRecord(data, 0, data.length);
09 baos.close();
10 dos.close();
11 }
12 catch (IOException e) {
13 System.out.println("-- Erro de IO");
14 }
15 catch (RecordStoreFullException e) {
16 System.out.println("-- Não existe espaço disponível");
17 }
18 catch( RecordStoreNotOpenException e ){
19 System.out.println("-- O Record Store esta fechado");
20 }
21 catch( RecordStoreException e ){
22 System.out.println("-- Outro erro");
23 }
24 }
|
O valor retornado pelo método addRecord é um identificador único para o registro adicionado. Os identificadores de registros começam em 1 e são incrementados a cada registro adicionado. Para obter o número identificador do próximo registro a ser adicionado utilize o método getNextRecordID, veja o código a seguir:
01
02 public int getNextRecordId() {
03 int toReturn = 0;
04 try {
05 toReturn = rs.getNextRecordID();
06 }
07 catch (RecordStoreFullException e) {
08 System.out.println("-- Não existe espaço disponível");
09 }
10 catch (RecordStoreNotOpenException e) {
11 System.out.println("-- O Record Store esta fechado");
12 }
13 catch (RecordStoreException e) {
14 System.out.println("-- Outro erro");
15 }
16 return toReturn;
17 }
|
Excluindo Registros com RMS
Para excluir um registro do record store execute o método deleteRecord, é necessário informa o id do registro a ser excluído como parâmetro para o método deleteRecord. Veja o código a seguir:
01 public void delete(int id) {
02 try {
03 rs.deleteRecord(id);
04 }
05 catch (RecordStoreFullException e) {
06 System.out.println("-- Não existe espaço disponível");
07 }
08 catch (RecordStoreNotOpenException e) {
09 System.out.println("-- O Record Store esta fechado");
10 }
11 catch (RecordStoreException e) {
12 System.out.println("-- Outro erro");
13 }
14 }
|
Recuperando Registros com RMS
Para recuperar um registro utilize o método getRecord. Veja o código a seguir:
01 public String getRecord(int id) {
02 String toReturn = "";
03 try {
04 int recordSize = rs.getRecordSize(id);
05 byte[] data = new byte[recordSize];
06 ByteArrayInputStream bais = new ByteArrayInputStream(data);
07 DataInputStream dis = new DataInputStream(bais);
08 int numBytes = rs.getRecord(id, data, 0);
09 toReturn = dis.readUTF();
10 bais.reset();
11 bais.close();
12 dis.close();
13 }
14 catch (IOException e) {
15 System.out.println("-- Erro de IO");
16 }
17 catch (ArrayIndexOutOfBoundsException e) {
18 System.out.println("-- Registro muito grande");
19 }
20 catch (InvalidRecordIDException e) {
21 System.out.println("-- ID inexistente");
22 }
23 catch (RecordStoreNotOpenException e) {
24 System.out.println("-- O Record Store esta fechado");
25 }
26 catch (RecordStoreException e) {
27 System.out.println("-- Outro erro");
28 }
29 return toReturn;
30 }
|
O primeiro parâmetro é o identificador do registro, o segundo parâmetro é o array de bytes onde o conteúdo do registro será copiado e o terceiro parâmetro é o offset de onde será iniciada a cópia. Assume-se que o array informado para o segundo parâmetro será grande o suficiente para armazenar o tamanho do registro. Utilize o método getRecordSize para obter o tamanho do registro e redimensionar o array de bytes antes de chamar o método getRecord.
Alterando Registros com RMS
Para alterar os dados de um registro utilize o método setRecord. Veja o código a seguir:
01 public void update(int id, String nome) {
02 try {
03 ByteArrayOutputStream baos = new ByteArrayOutputStream();
04 DataOutputStream dos = new DataOutputStream(baos);
05 dos.writeUTF(nome);
06 dos.flush();
07 byte[] data = baos.toByteArray();
08 rs.setRecord(id, data, 0, data.length);
09 }
10 catch (IOException e) {
11 System.out.println("-- Erro de IO");
12 }
13 catch (ArrayIndexOutOfBoundsException e) {
14 System.out.println("-- Registro muito grande");
15 }
16 catch (InvalidRecordIDException e) {
17 System.out.println("-- ID inexistente");
18 }
19 catch (RecordStoreNotOpenException e) {
20 System.out.println("-- O Record Store esta fechado");
21 }
22 catch (RecordStoreException e) {
23 System.out.println("-- Outro erro");
24 }
25 }
|
Perceba que não é possível alterar apenas parte dos dados de um registro. O registro é substituido pelo novo registro.
Navegando nos Registros com RMS
Existem duas formas de navegar nos registros de um record store. Pode-se navegar utilizando um loop simples entre os registros ou utilizar a classe RecordEnumeration do RMS. Veja o código a seguir:
01 public void printLoop() {
02 try {
03 for (int i = 1; i <= rs.getNumRecords(); i++) {
04 System.out.println("-- getRecord = " + getRecord(i));
05 }
06 }
07 catch (RecordStoreNotOpenException ex) {
08 System.out.println("-- O Record Store esta fechado");
09 }
10 }
11
12 public void printEnum() {
13 try {
14 RecordEnumeration re = rs.enumerateRecords(null, null, false);
15 while (re.hasNextElement()) {
16 System.out.println("-- getRecord = " + getRecord(re.nextRecordId()));
17 }
18 }
19 catch (InvalidRecordIDException ex) {
20 System.out.println("-- ID inexistente");
21 }
22 catch (RecordStoreNotOpenException ex) {
23 System.out.println("-- O Record Store esta fechado");
24 }
25 }
|
Para uma navegação simples um loop funciona perfeitamente. No entanto, se você deseja ordenar e/ou localizar os registros, a classe RecordEnumeration possui recursos poderosos para executar a navegação. O que torna a classe RecordEnumerator tão especial é o fato que ela pode navegar nos registros utilizando um RecordFilter (para localizar) ou um RecordComparator (para ordenar). Por exemplo, utilizando a interface RecordFilter, é possível filtrar os registros que começam com alguma substring ou, utilizando a interface RecordComparator, é possível ordenar os registros na ordem alfabética.
A classe RecordEnumerator não possui um construtor. Para obter uma instância, execute o método enumerateRecords da classe RecordStore. Os parâmetros desse método são, respectivamente, um RecordFilter, um RecordComparator e um booleano que indica se os registros alterados por outros MIDlets serão visualizados no enumerator.
Ordenando Registros com RMS
A interface RecordComparator possui um único método que compara dois arrays de bytes. Crie uma classe que implementa essa interface, implemente o método compare e passe uma instância da sua classe para o RecordEnumerator. Veja o código a seguir:
01 package rms;
02
03 import javax.microedition.rms.RecordComparator;
04
05 public class ComparatorString implements RecordComparator {
06 public int compare(byte[] rec1, byte[] rec2) {
07 String s1 = new String(rec1);
08 String s2 = new String(rec2);
09 int comparation = s1.compareTo(s2);
10 if (comparation == 0)
11 return RecordComparator.EQUIVALENT;
12 else
13 if (comparation > 0)
14 return RecordComparator.PRECEDES;
15 else
16 return RecordComparator.FOLLOWS;
17 }
18 }
|
Utilize agora a classe criada para ordenar os registros. Veja o código a seguir:
01 public void printEnumOrder() {
02 try {
03 ComparatorString sc = new ComparatorString();
04 RecordEnumeration re = rs.enumerateRecords(null, sc, false);
05 while (re.hasNextElement()) {
06 System.out.println("-- getRecord = " + getRecord(re.nextRecordId()));
07 }
08 }
09 catch (InvalidRecordIDException ex) {
10 System.out.println("-- ID inexistente");
11 }
12 catch (RecordStoreNotOpenException ex) {
13 System.out.println("-- O Record Store esta fechado");
14 }
15 }
|
Filtrando Registros com RMS
A interface RecordFilter possui um único método que recebe um array de bytes como parâmetro. Crie uma classe que implementa essa interface, implemente o método matches e passe uma instância da sua classe para o RecordEnumerator. Veja o código a seguir:
01 package rms;
02
03 import javax.microedition.rms.RecordFilter;
04
05 public class RecordFilterNome implements RecordFilter {
06
07 private String nome;
08
09 public RecordFilterNome(String nome) {
10 this.nome = nome.toLowerCase();
11 }
12
13 public boolean matches(byte[] candidate) {
14 String nomeCandidate = new String(candidate).toLowerCase();
15 return (nome != null && nomeCandidate.indexOf(nome) != -1);
16 }
17
18 }
|
Esta classe irá filtrar todos os registros que possuem o nome igual ao nome passado no parâmetro do construtor. Utilize agora a classe criada para filtrar os registros. Veja o código a seguir:
01 public void printEnumFilter() {
02 try {
03 RecordFilterNome rfn = new RecordFilterNome("Zezinho");
04 RecordEnumeration re = rs.enumerateRecords(rfn, null, false);
05 while (re.hasNextElement()) {
06 System.out.println("-- getRecord = " + getRecord(re.nextRecordId()));
07 }
08 }
09 catch (InvalidRecordIDException ex) {
10 System.out.println("-- ID inexistente");
11 }
12 catch (RecordStoreNotOpenException ex) {
13 System.out.println("-- O Record Store esta fechado");
14 }
15 }
|
Classe gerada
Veja a seguir o código execute da classe gerada.
01 package rms;
02
03 import javax.microedition.rms.*;
04 import java.io.*;
05
06 public class TutorialRecordStore {
07
08 private static String RS_NAME = "Teste";
09 private RecordStore rs = null;
10
11 public void execute() {
12 open();
13 System.out.println("-- open");
14 add("Marcio");
15 System.out.println("-- add");
16 System.out.println("-- proximo id = " + getNextRecordId());
17 add("Zezinho");
18 System.out.println("-- add novamente");
19
20 System.out.println("-- proximo id = " + getNextRecordId());
21
22 System.out.println("-- printLoop");
23 printLoop();
24
25 update(1, "Marcio Guedes");
26 System.out.println("-- update");
27
28 System.out.println("-- printEnum");
29 printEnum();
30
31 System.out.println("-- printEnumOrder");
32 printEnumOrder();
33
34 System.out.println("-- printEnumFilter");
35 printEnumFilter();
36
37 close();
38 System.out.println("-- close");
39 }
40
41 ...
42 }
|
O método execute da nossa classe criada irá fazer chamada de todos os nossos métodos que encapsulam a framework do RMS. Veja a seguir a saída do método execute:
-- open
-- add
-- proximo id = 2
-- add novamente
-- proximo id = 3
-- printLoop
-- getRecord = Marcio
-- getRecord = Zezinho
-- update
-- printEnum
-- getRecord = Zezinho
-- getRecord = Marcio Guedes
-- printEnumOrder
-- getRecord = Marcio Guedes
-- getRecord = Zezinho
-- printEnumFilter
-- getRecord = Zezinho
-- close
|
É isso aí. Você pode baixar aqui no GUJ os código utilizados nesse tutorial.
|
|
|