Os dados manipulados pelas aplicações do mundo real são de suma importância. Por isto eles precisam ser armazenados em algum lugar para serem acessados quando necessário. Um exemplo disto, é um cadastro de dados pessoais, onde você armazena o nome, endereço e telefone de pessoas para serem acessados mais tarde.

Como fazer isto em um dispositivo móvel? Simples, pois cada dispositivo móvel mantêm uma área dedicada de memória para armazenamento dos dados. Neste artigo, você vai começar a manipular com esta área utilizando a API RMS que o MIDP (Mobile Information Device Profile) disponibiliza. Inicialmente, será apresentado o modelo de armazenamento e como manipular com estes dados. Um exemplo de código é apresentado para você aprender os conceitos e construir um catálogo de endereços para o seu celular.

O modelo de armazenamento de dados

O RMS (Record Management System) suporta a criação e manipulação de vários registros para armazenamento de dados chamados de record store. Um record store é identificado pelo nome que consiste de no máximo 32 caracteres. Cada registro pode ter um ou mais outros registros.

O record store é criado pelo MIDlet. MIDlets dentro de um mesmo MIDlet Suite podem compartilhar os registros. Portanto, um record store deve conter um nome único dentro do MIDlet Suite. O mesmo nome pode ser utilizado em diferentes MIDlets Suites.

Quando o MIDlet for removido do dispositivo, todos os record stores criados nele também são removidos. Neste caso, obviamente todos os dados são perdidos. Por esta razão, o desenvolvedor deve considerar isto no projeto da aplicação incluindo mensagens de alerta ou confirmação para informar aos usuários a perda dos dados se a aplicação for removida. As aplicações devem também incluir um mecanismo de backup dos dados para outra localização.

As operações para manipulação do record store são:

  • Adicionar um registro
  • Deletar um registro
  • Alterar um registro
  • Recuperar um registro
  • Enumerar todos os registros

Os registros são identificados por um record ID, que é a chave primária. O record ID é do tipo inteiro (int). O primeiro registro adicionado recebe o ID 1 e os demais recebem o ID do último registro somado de 1. O RMS não suporta tipos característicos de banco de dados, tais como tabelas, linhas colunas etc.

Um registro é um array de bytes do tipo byte [ ]. Por causa disto, as aplicações devem converter seus dados para array de bytes ao escrever no registro e ao ler os dados a conversão inversa deve ser feita.

Exceções relacionadas ao acesso

O RMS define várias exceções relacionadas ao acesso aos dados. Todas elas pertencem ao pacote javax.microedition.rms. Veja a Figura 1.

pwm-25ago-rms_image001.jpg
Figura 1. Exceções do RMS

Ao desenvolver uma aplicação é importante tratar estas exceções.

A manipulação dos dados

Como mencionado anteriormente, conversões ao adicionar e recuperar os dados no registro precisam ser feitas devido ao armazenamento em array de bytes. O método addAddress do código de exemplo ilustra esta conversão. Uma função bastante utilizada nas aplicações é a recuperação de um determinado registro. Por exemplo, em um cadastro de dados pessoais o usuário pode querer entrar com o nome da pessoa e obter todos os dados armazenados da mesma, tais como, endereço, telefone, estado civil, sexo etc. O RMS disponibiliza duas maneiras para recuperar os dados de um registro específico.

Utilizar o record ID

Recuperar vários registros e procurar pelo qual você interessa através da enumeração

Na primeira opção, você precisa ter o identificador (ID) do registro. Portanto, você precisa guardá-lo de alguma forma. Isto não é conveniente e nem prático quando se tratar de um número muito grande de registros. A maneira mais fácil é usar a enumeração principalmente quando você não sabe os IDs que você quer e depois examinar cada registro para verificar qual você procura. A classe RecordStore define o método enumerateRecords que retorna alguns ou todos os registros de um record store, dependendo do que você passar como parâmetro. O método findAddress do código de exemplo ilustra o uso da enumeração para percorrer o record store.

Algumas vezes você pode querer selecionar um subconjunto de registros e não somente um registro específico. Neste caso, você pode utilizar a enumeração para recuperar os registros que satisfaçam algum critério. O primeiro argumento do método enumerateRecords especifica o filtro que é um objeto de RecordFilter. RecordFilter é uma classe que implementa a interface RecordFilter para identificar critérios utilizados no filtro.

O segundo argumento do método enumerateRecords é utilizado para comparar registros para determinar a sua ordem. Você poderá utilizá-lo quando quiser ordenar os registros seguindo algum critério, por exemplo, recuperar todos os nomes das pessoas em ordem alfabética.

Record listeners

As aplicações podem receber notificações quando um registro é adicionado, removido ou alterado. Para isto, você utiliza a classe RecordStore que permite adicionar e remover listeners utilizando os métodos addRecordListener e removeRecordListener. No exemplo apresentado, não utilizamos esta funcionalidade, mas você poderá incluí-la se quiser. O importante é saber que existe e se você precisar há uma maneira de receber estas notificações.

A performance

O desempenho ao acessar o record store é bastante lento, o que não deixa de ser um problema. Os desenvolvedores devem considerar isto ao escolher as alternativas de armazenamento dos dados para a sua aplicação. Uma alternativa viável para aplicações com muitos dados é utilizar um banco de dados em um servidor e acessá-lo através da conexão utilizando o protocolo HTTP (Hiper Text Transfer Protocol). Neste caso, o uso do RMS é viável para aplicações que não manipulam grande quantidade de dados e o desempenho não é tão importante. Um exemplo típico destas aplicações é o catálogo de endereços ilustrado no exemplo.

A seguir o código que implementa o catálogo de endereços utilizando o RMS.



    import java.io.ByteArrayInputStream;

    import java.io.ByteArrayOutputStream;

    import java.io.DataInputStream;

    import java.io.DataOutputStream;

    import java.io.IOException;

     

    import javax.microedition.lcdui.Alert;

    import javax.microedition.lcdui.AlertType;

    import javax.microedition.lcdui.Command;

    import javax.microedition.lcdui.CommandListener;

    import javax.microedition.lcdui.Display;

    import javax.microedition.lcdui.Displayable;

    import javax.microedition.lcdui.Form;

    import javax.microedition.lcdui.StringItem;

    import javax.microedition.lcdui.TextField;

    import javax.microedition.midlet.MIDlet;

    import javax.microedition.midlet.MIDletStateChangeException;

    import javax.microedition.rms.InvalidRecordIDException;

    import javax.microedition.rms.RecordEnumeration;

    import javax.microedition.rms.RecordStore;

    import javax.microedition.rms.RecordStoreException;

    import javax.microedition.rms.RecordStoreFullException;

    import javax.microedition.rms.RecordStoreNotFoundException;

    import javax.microedition.rms.RecordStoreNotOpenException;

     

     

    public class AddressCatalog extends MIDlet implements CommandListener {

           private Display;

           private Form mainForm;

           private TextField tbName; 

           private TextField tbAddress;

           private TextField tbQuestion;

           private Command cmdExit;   // Botão para sair do MIDlet

           private Command cmdSave;   // Botão para salvar no record store

           private Command cmdFind;   // Botão para encontrar um endereço

           private Command cmdRem;    // Botão para remover um nome do cadastro

           private Command cmdAdd;    // Botão para adicionar o endereço

           private StringItem strOpt;

           private Command cmdFindOK;

           private Command cmdFindBack;

           private Command cmdBack;

           private Command cmdRemOK;

           private Command cmdRemBack;

           private RecordStore rs;   // Objeto para manipulação do Record Store

          

           public AddressCatalog () throws RecordStoreNotFoundException, 
              RecordStoreFullException, RecordStoreException {

                 // Abre o Record Store chamado "AddressCatalog"

                 rs = RecordStore.openRecordStore("AddressCatalog", true);

           }

     

           protected void startApp() throws MIDletStateChangeException {

                 // Tela principal

                 mainScreen();

           }

           private void mainScreen() {

                 // Desenha a tela principal

                 mainForm = new Form ("Catálogo de endereços");

                 strOpt = new StringItem ("Escolha a opção no Menu", null);

                

                 cmdAdd = new Command ("Adicionar",Command.SCREEN,1);

                 cmdFind = new Command ("Encontrar", Command.SCREEN,1);

                 cmdRem = new Command ("Remover", Command.SCREEN,1);

                 cmdExit = new Command ("Sair",Command.SCREEN,1);

                

                 mainForm.append(strOpt);

                 mainForm.addCommand(cmdAdd);

                 mainForm.addCommand(cmdFind);

                 mainForm.addCommand(cmdRem);

                 mainForm.addCommand(cmdExit);

                 mainForm.setCommandListener(this);

     

                 display = Display.getDisplay(this);

                 display.setCurrent(mainForm);

           }

     

           protected void pauseApp() { }

     

           protected void destroyApp(boolean arg0) throws MIDletStateChangeException{ }

     

           //Método responsável por verificar o comando e chamar o método apropriado

           public void commandAction(Command cmd, Displayable disp) {

                 // Comando Sair

                 if (cmd == cmdExit){

                        try {

                               destroyApp(true);

                        } catch (MIDletStateChangeException e) {

                               e.printStackTrace();

                        }

                        notifyDestroyed();

                 } // Commando Adicionar

                   else if (cmd == cmdAdd) {

                        addAddress();

                 } // Comando Salvar

                   else if (cmd == cmdSave) {

                        try {

                               saveOpt(tbName.getString(), tbAddress.getString());

                        } catch (RecordStoreException e) {

                               e.printStackTrace();

                        }

                 } // Comando Encontrar

                   else if (cmd == cmdFind) {

                        findOpt();

                 } // Commando Remover

                   else if (cmd == cmdRem) {

                        remOpt();

                   }

                   // Comando OK da opção Encontrar

                   else if (cmd == cmdFindOK) {

                        try {

                               findAddress(tbQuestion.getString());

                        } catch (RecordStoreException e) {

                               // TODO Auto-generated catch block

                               e.printStackTrace();

                        } catch (IOException e) {

                               // TODO Auto-generated catch block

                               e.printStackTrace();

                        }

                 } // Comando Voltar

                 else if (cmd == cmdFindBack || cmd == cmdRemBack || cmd == cmdBack)
                 {

                        mainScreen();

                 } // Comando OK da opção Listar

                    else if (cmd == cmdRemOK) {

                        try {

                               remAddress();

                        } catch (IOException e1) {

                                      // TODO Auto-generated catch block

                                      e1.printStackTrace();

                        } catch (RecordStoreException e) {

                               // TODO Auto-generated catch block

                               e.printStackTrace();

                        }

           }

           }

     

           // Método responsável pela entrada de dados da opção Listar

           private void remOpt() {

                 Form form = new Form ("Remove endereço");

                 tbName = new TextField ("Entre com o nome", "", 30,TextField.ANY);

                 cmdRemOK = new Command("OK",Command.SCREEN,1);

                 cmdRemBack = new Command("Voltar",Command.SCREEN,1);

                 form.append(tbName);

                 form.addCommand(cmdRemOK);

                 form.addCommand(cmdRemBack);

                 form.setCommandListener(this);

                 display.setCurrent(form);

           }

     

           // Método responsável por remover um endereço do catálogo

           private void remAddress() throws RecordStoreException, IOException {

                 RecordEnumeration re = null; 

                 boolean findAddress = false;

     

                 // Recupera os registros usando enumeração

                 try {

                        re = rs.enumerateRecords(null, null, false);

                  } catch (RecordStoreNotOpenException e1) {

                        // TODO Auto-generated catch block

                        e1.printStackTrace();

                 }

     

                 // Procura pelo registro

                 if (re.numRecords() > 0) {

                        ByteArrayInputStream bais = null;

                        DataInputStream dis = null;

                        String nameRMS = null;

                       

                        // Procura pelo endereço enquanto houver registros no RMS

                        byte [] record = re.nextRecord();

                        int id = 0;

                       

                        while (re.hasNextElement()) {

                               System.out.println (id);

                               bais = new ByteArrayInputStream (record);

                               dis = new DataInputStream(bais);

                               nameRMS = dis.readUTF();

                               String name = tbName.getString();

                               if (name.compareTo(nameRMS)== 0) { // Encontrou

                                      findAddress = true;

                                      // Recupera o ID corrente

                                      record = re.nextRecord();

                                      id = re.previousRecordId();

                                      // Remove o endereço pelo ID

                                      rs.deleteRecord(id);

                                      showMessage ("Sucesso!");

                                      break;

                               }

                               record = re.nextRecord();

                        }

                        if (!findAddress) {

                               showMessage ("Endereço não cadastrado");

                        }

                 } else {

                        showMessage ("Endereço não cadastrado");

                 }

           }

     

           // Método responsável pela entrada de dados para adição no catálogo

           private void addAddress() {

                 Form form = new Form ("Adiciona endereços");

                 tbName = new TextField ("Nome", "", 30,TextField.ANY);

                 tbAddress = new TextField ("Endereço", "",50,TextField.ANY);

                 cmdSave = new Command ("Salvar",Command.SCREEN,1);

                 form.append(tbName);

                 form.append(tbAddress);

                 form.addCommand(cmdExit);

                 form.addCommand(cmdSave);

                 form.setCommandListener(this);

     

                 display = Display.getDisplay(this);

                 display.setCurrent(form);

           }

     

           // Método responsável por encontrar o endereço a partir do nome

           private void findAddress(String name) throws RecordStoreException, 
             IOException {

                 RecordEnumeration re = null; 

                 boolean findAddress = false;

     

                 // Recupera os registros usando enumeração

                 try {

                        re = rs.enumerateRecords(null, null, false);

                 } catch (RecordStoreNotOpenException e1) {

                        // TODO Auto-generated catch block

                        e1.printStackTrace();

                 }

     

                 // Procura pelo registro

                 if (re.numRecords() > 0) {

                        ByteArrayInputStream bais = null;

                        DataInputStream dis = null;

                        String nameRMS = null;

     

                   //Procura pelo endereço enquanto houver registros no record store

                        byte [] record = re.nextRecord();

                        while (re.hasNextElement()) {

                               bais = new ByteArrayInputStream (record);

                               dis = new DataInputStream(bais);

                               nameRMS = dis.readUTF();

                               if (name.compareTo(nameRMS)== 0) { // Encontrou

                                      findAddress = true;

                                      System.out.println(nameRMS);

                                      showAddress(dis.readUTF());

                                      break;

                               }

                               record = re.nextRecord();

                        }

                        if (!findAddress)

                               showMessage ("Endereço não cadastrado");

                 } else {

                        showMessage ("Endereço não cadastrado");

                 }

           }

     

           // Método responsável por mostrar o endereço encontrado

           private void showAddress(String addressRMS) {

                 StringItem strAddress;

                 Form form = new Form("Mostrar endereço");

                 cmdBack = new Command("Voltar",Command.SCREEN,1);

                

                 strAddress = new StringItem("Endereço:", addressRMS);

                 form.append(strAddress);

                 form.addCommand(cmdBack);

                 form.setCommandListener(this);

                 display.setCurrent(form);

           }

     

           // Método responsável pela entrada de dados para procura

           private void findOpt() {

                 Form form = new Form("Achar endereço");

                 cmdFindOK = new Command("OK",Command.SCREEN,2);

                 cmdFindBack = new Command("Voltar",Command.SCREEN,2);

                

                 tbQuestion = new TextField("Entre com o nome", "", 30, 
                 TextField.ANY);

                 form.append(tbQuestion);

                 form.addCommand(cmdFindOK);

                 form.addCommand(cmdFindBack);

                 form.setCommandListener(this);

                 display.setCurrent(form);

           }

     

       //Método responsável por adicionar os dados (nome e endereço) no record store

           private void saveOpt(String name, String address) throws 
             RecordStoreException {

                 ByteArrayOutputStream baos = new ByteArrayOutputStream();

                 DataOutputStream dos = new DataOutputStream(baos);

                

                 try {

                        dos.writeUTF(name);

                        dos.writeUTF(address);

                 } catch (IOException e) {

                        e.printStackTrace();

                 }

     

                 // Adiciona os dados convertendo-os para array de bytes

                 rs.addRecord(baos.toByteArray(), 0, baos.toByteArray().length);

                 showMessage ("Sucesso !");

           }

     

           // Método responsável por apresentar mensagens na tela

           private void showMessage(String msg) {

                 Alert alert = new Alert("Mensagem", msg, null, 
                    AlertType.CONFIRMATION);

                 display.setCurrent(alert,mainForm);

           }

    }

Conclusão

O armazenamento dos dados é de suma importância para uma aplicação do mundo real. Existem várias maneiras de armazenar os dados. Entre elas está o RMS disponibilizado pelo MIDP que permite armazenar os dados em record store que guardam os dados em array de bytes. Os registros podem ser compartilhados entre os MIDlets de um mesmo MIDlet Suite e são identificados pelo nome.

A aplicação deve utilizar mecanismos de conversão dos tipos de dados específicos para array de bytes e vice-versa ao escrever e ler os dados respectivamente. O record store pode ser acessado pelo seu ID ou através de enumeration que é a melhor opção para recuperar registros. Utilizando enumeração você recupera todos os registros de um record store e registros que satisfaçam critérios que você especificar. Outra facilidade disponibilizada pela enumeração é recuperar dados de uma forma ordenada. Embora a performance em utilizar o RMS seja um problema, este sistema poderá ser utilizado em pequenas aplicações que armazenam poucos dados cujo desempenho não seja tão importante.

Saiu na DevMedia!

  • Programe com o Node.js:
    Aqui você vai se familiarizar com a programação com o NodeJs ao passo que acrescenta em seu portfólio uma aplicação Fullstack em JavaScript, API RESTful e cliente web com NodeJs e React.

Saiba mais sobre Banco de Dados ;)