Você está na página 1de 164

Módulo 5

Desenvolvimento de Aplicações Móveis

Lição 1
Introdução

Versão 1.0 - Set/2007


JEDITM

Autor Necessidades para os Exercícios


A. Oliver de Guzman Sistemas Operacionais Suportados
NetBeans IDE 5.5 para os seguintes sistemas operacionais:
• Microsoft Windows XP Profissional SP2 ou superior
• Mac OS X 10.4.5 ou superior
Equipe • Red Hat Fedora Core 3
Rommel Feria • Solaris™ 10 Operating System (SPARC® e x86/x64 Platform Edition)
John Paul Petines NetBeans Enterprise Pack, poderá ser executado nas seguintes plataformas:
• Microsoft Windows 2000 Profissional SP4
• Solaris™ 8 OS (SPARC e x86/x64 Platform Edition) e Solaris 9 OS (SPARC e
x86/x64 Platform Edition)
• Várias outras distribuições Linux

Configuração Mínima de Hardware


Nota: IDE NetBeans com resolução de tela em 1024x768 pixel
Sistema Operacional Processador Memória HD Livre
Microsoft Windows 500 MHz Intel Pentium III 512 MB 850 MB
workstation ou equivalente
Linux 500 MHz Intel Pentium III 512 MB 450 MB
workstation ou equivalente
Solaris OS (SPARC) UltraSPARC II 450 MHz 512 MB 450 MB
Solaris OS (x86/x64 AMD Opteron 100 Série 1.8 GHz 512 MB 450 MB
Platform Edition)
Mac OS X PowerPC G4 512 MB 450 MB

Configuração Recomendada de Hardware

Sistema Operacional Processador Memória HD Livre


Microsoft Windows 1.4 GHz Intel Pentium III 1 GB 1 GB
workstation ou equivalente
Linux 1.4 GHz Intel Pentium III 1 GB 850 MB
workstation ou equivalente
Solaris OS (SPARC) UltraSPARC IIIi 1 GHz 1 GB 850 MB
Solaris OS (x86/x64 AMD Opteron 100 Series 1.8 GHz 1 GB 850 MB
Platform Edition)
Mac OS X PowerPC G5 1 GB 850 MB

Requerimentos de Software
NetBeans Enterprise Pack 5.5 executando sobre Java 2 Platform Standard Edition
Development Kit 5.0 ou superior (JDK 5.0, versão 1.5.0_01 ou superior), contemplando
a Java Runtime Environment, ferramentas de desenvolvimento para compilar, depurar,
e executar aplicações escritas em linguagem Java. Sun Java System Application Server
Platform Edition 9.
• Para Solaris, Windows, e Linux, os arquivos da JDK podem ser obtidos para
sua plataforma em http://java.sun.com/j2se/1.5.0/download.html
• Para Mac OS X, Java 2 Plataform Standard Edition (J2SE) 5.0 Release 4, pode
ser obtida diretamente da Apple's Developer Connection, no endereço: http://
developer.apple.com/java (é necessário registrar o download da JDK).

Para mais informações: http://www.netbeans.org/community/releases/55/relnotes.html

Desenvolvimento de Aplicações Móveis 2


JEDITM

Colaboradores que auxiliaram no processo de tradução e revisão


Aécio Júnior Fábio Bombonato Luiz Fernandes de Oliveira Junior
Alexandre Mori Fabrício Ribeiro Brigagão Marco Aurélio Martins Bessa
Alexis da Rocha Silva Francisco das Chagas Maria Carolina Ferreira da Silva
Allan Souza Nunes Frederico Dubiel Massimiliano Giroldi
Allan Wojcik da Silva Herivelto Gabriel dos Santos Mauro Cardoso Mortoni
Anderson Moreira Paiva Jacqueline Susann Barbosa Paulo Afonso Corrêa
Andre Neves de Amorim João Vianney Barrozo Costa Paulo Oliveira Sampaio Reis
Angelo de Oliveira Kefreen Ryenz Batista Lacerda Pedro Henrique Pereira de Andrade
Antonio Jose R. Alves Ramos Kleberth Bezerra G. dos Santos Ronie Dotzlaw
Aurélio Soares Neto Leandro Silva de Morais Seire Pareja
Bruno da Silva Bonfim Leonardo Ribas Segala Sergio Terzella
Carlos Fernando Gonçalves Lucas Vinícius Bibiano Thomé Vanessa dos Santos Almeida
Denis Mitsuo Nakasaki Luciana Rocha de Oliveira Robson Alves Macêdo

Auxiliadores especiais

Revisão Geral do texto para os seguintes Países:


• Brasil – Tiago Flach
• Guiné Bissau – Alfredo Cá, Bunene Sisse e Buon Olossato Quebi – ONG Asas de Socorro

Coordenação do DFJUG

• Daniel deOliveira – JUGLeader responsável pelos acordos de parcerias


• Luci Campos - Idealizadora do DFJUG responsável pelo apoio social
• Fernando Anselmo - Coordenador responsável pelo processo de tradução e revisão,
disponibilização dos materiais e inserção de novos módulos
• Rodrigo Nunes - Coordenador responsável pela parte multimídia
• Sérgio Gomes Veloso - Coordenador responsável pelo ambiente JEDITM (Moodle)

Agradecimento Especial
John Paul Petines – Criador da Iniciativa JEDITM
Rommel Feria – Criador da Iniciativa JEDITM

Desenvolvimento de Aplicações Móveis 3


JEDITM

1. Objetivos
Nesta lição, discutiremos as características dos dispositivos móveis e a forma de iniciar o
desenvolvimento de aplicações para estes dispositivos. Realizaremos uma introdução à Java
Platform, Micro Edition (Java ME) incluindo a importância das configurações e perfis.

Ao final desta lição, o estudante será capaz de:


• Identificar as características dos dispositivos móveis
• Descrever a arquitetura JME
• Conhecer a personalização das configurações e perfis (CLDC e CDC)
• Identificar as bibliotecas fornecidas pelo MIDP
• Descrever o ciclo de vida de um MIDlet

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. Dispositivos Móveis
Dispositivos móveis podem variar em tamanho, projeto e layout, mas eles possuem algumas
características em comum que são totalmente diferentes de sistemas desktop.
• Pequenos em tamanho
Dispositivos móveis são pequenos em tamanho. Consumidores desejam dispositivos pequenos
pela mobilidade e conveniência.

• Memória Limitada
Dispositivos móveis também possuem pouca memória, tanto primária (RAM) quanto
secundária (disco). Esta limitação é um dos fatores que afetam a escrita de classes para estes
tipos de dispositivos. Com quantidade limitada de memória, devemos fazer considerações
especiais acerca da conservação no uso de recursos preciosos.

• Poder de processamento limitado


Sistemas móveis não são poderosos como são os sistemas desktop quanto a sua organização.
Tamanho, tecnologia e orçamento são alguns dos fatores que influenciam a condição desses
recursos. Como o disco de armazenamento e RAM, apenas pequenos pacotes se adequam a
estes recursos.
• Baixo consumo de energia
Dispositivos móveis possuem baixo consumo de energia em relação às máquinas desktop.
Estes dispositivos necessitam poupar o uso de energia, pois possuem um limitado
abastecimento através de baterias.
• Robusto e confiável
Por serem dispositivos móveis provavelmente serão carregados. Precisam ser robustos o
suficiente para suportarem a força de impacto, movimento e ocasionalmente quedas.
• Conectividade limitada
Dispositivos móveis têm baixa largura de banda, alguns deles não suportam conexão. Outros
destes usam conexões de rede sem fio.
• Curto tempo de inicialização
Estes dispositivos inicializam-se em segundos. Tomemos o caso de telefones móveis: eles se
iniciam em segundos e as pessoas não ficam com estes desligados mesmo à noite. PDAs
inicializam no segundo em que é pressionado o botão de ligar.

Desenvolvimento de Aplicações Móveis 5


JEDITM

3. Visão sobre a Java ME


3.1. Plataforma Java

Java foi criado em 1991 por James Gosling, da Sun Microsystems. Inicialmente chamada de OAK,
em homenagem a árvore que ficava do lado de fora vista da janela de Gosling, este nome foi
modificado para Java porque já existia uma linguagem chamada OAK.

A motivação original para Java estava na necessidade para uma linguagem independente de
plataforma que fosse embarcada em vários produtos eletrônicos de consumo como torradeiras e
refrigeradores. Um dos primeiros projetos desenvolvidos usando Java foi um controle remoto
pessoal chamado de Star 7.

Nessa mesma direção, e ao mesmo tempo, a World Wide Web e a Internet estavam ganhando
popularidade. Gosling tornava Java capaz de ser usada para a programação para Internet.

Com o lançamento da versão 1.2, a plataforma Java foi classificada em várias plataformas: Java
Standard Edition (Java SE), Java Enterprise Edition (Java EE), Java Micro Edition (Java ME) e Java
Card API.

Java SE – Java Platform, Standard Edition aplicações desktop


Java EE – Java Platform, Enterprise Edition aplicações corporativas com ênfase no modelo de
desenvolvimento server-side incluindo servlets, JSP,
EJB e XML
Java ME – Java Platform, Micro Edition móveis e dispositivos de mão
JavaCard Cartões com chip

Servidores
Servers
Máquinas
desktop
Dispositivos
Pacotes
Opcionais
de alto
consumo Dispositivos
Pacotes
de baixo
Opcionais consumo

Cartões
com chip
Java EE Personal Profile
Java SE
Foundation Profile MIDP

Java
CDC CLDC
Card

Máquina Virtual Java KVM Card VM

Java Micro Edition (Java ME)

Figura 1: A plataforma Java

Desenvolvimento de Aplicações Móveis 6


JEDITM

3.2 Visão Geral do JME

A Plataforma Java Micro Edition (Java ME) é um conjunto de especificações e tecnologias que têm
o foco em dispositivos pessoais. Estes dispositivos têm uma quantidade limitada de memória,
menor poder de processamento, pequenas telas e baixa velocidade de conexão.
Com a proliferação dos dispositivos pessoais, desde telefones, PDAs, videogames portáteis a
aplicações domésticas, Java fornece um único ambiente portátil de desenvolvimento e execução
destes dispositivos.
Classes JME, assim como todas as classes Java, são interpretadas. Elas são compiladas em byte
codes e interpretadas por uma Máquina Virtual Java (JVM). Isto significa que estas classes não
são afetadas pelas peculiaridades de cada dispositivo. O JME fornece uma interface consistente
com os dispositivos. As aplicações não têm que ser recompiladas para poderem ser executadas
em diferentes aparelhos.
No núcleo do JME estão uma configuração e perfis. Uma configuração define um ambiente de
execução básico para um sistema JME. Isto define as características das bibliotecas principais, da
máquina virtual, segurança e comunicação em rede.

Aplicações

Pacotes OEM
Perfis
Opcionais APIs
Bibliotecas
Configuração {
Máquina Virtual Java

Sistema Operacional do Dispositivo

Figura 2: Arquitetura do JME

Um perfil adiciona uma biblioteca para certas classes de dispositivos. Os perfis fornecem
bibliotecas da API de interface com o usuário, persistência e mensagens, entre outras.
Um conjunto ou pacote opcional de bibliotecas que fornecem classes funcionais adicionais. A
inclusão destes pacotes no JME pode variar porque depende da capacidade do dispositivo. Por
exemplo, alguns dispositivos MIDP não possuem Bluetooth, logo as APIs de Bluetooth não são
incluídas nestes dispositivos.

3.3 Configuração

Uma configuração define características mínimas de um ambiente de execução Java completo.


Para garantir ótima portabilidade e interoperabilidade entre vários tipos de requisitos de recursos
de dispositivos (restrições de memória, processador e conexão), as configurações não contêm as
mesmas características opcionais. Uma configuração JME define um complemento mínimo da
tecnologia Java. Ela baseia-se nos perfis para definir bibliotecas adicionais (opções possíveis) para
uma determinada categoria de dispositivo.
Uma configuração define:
– o subconjunto da linguagem de programação Java
– a funcionalidade da Máquina Virtual Java (JVM)
– bibliotecas do núcleo da plataforma
– características de segurança e comunicação em rede

Desenvolvimento de Aplicações Móveis 7


JEDITM

3.4 Perfis

Um perfil define um conjunto adicional de bibliotecas e características de empresas, de categoria,


de dispositivo ou de indústria. Enquanto uma configuração define uma base de bibliotecas, perfis
definem as bibliotecas que são importantes para construir aplicações efetivas. Estas bibliotecas
incluem a interface com o usuário, comunicação em rede e classes de armazenamento.

Desenvolvimento de Aplicações Móveis 8


JEDITM

4. CLDC
A Configuração de Dispositivos de Conexão Limitada (Connected Limited Device Configuration –
CLDC) define e endereça as seguintes áreas:
• Características da linguagem Java e Máquina Virtual (JVM)
• Bibliotecas de núcleo (java.lang.*, java.util.*)
• Input/Output (java.io.*)
• Segurança
• Comunicação em rede
• Internacionalização

4.1. Características Removidas

Algumas características do JSE que foram removidas do CLDC:


• finalização de instâncias de classes
• exceções assíncronas
• algumas classes de erros
• carregadores de classes definidas pelo usuário
• reflexão
• Java Native Interface (JNI)
• grupos de processos e processos daemon
Reflexão, Java Native Interface (JNI) e carregadores de classes definidas pelo usuário são
potenciais falhas de segurança. JNI exigem muita memória e pode não ser suportada por
dispositivos móveis de pouca memória.

4.2. Características dos Dispositivos CLDC

Os dispositivos atingidos pelo CLDC possuem estas características:


• no mínimo 192kb de memória para a plataforma Java (160kb de memória não-volátil para
Máquina Virtual e bibliotecas e 32kb de memória volátil para execução da Máquina Virtual)
• processador de 16 ou 32 bits
• baixo consumo de energia (normalmente os que utilizam baterias)
• conexão limitada ou intermitente com velocidade também limitada (normalmente wireless)
A CLDC não define instalação da aplicação e ciclo de vida, interfaces com o usuário e tratamento
de eventos. Está para os perfis abaixo da CLDC definir estas áreas. Em particular, a especificação
MIDP é que define uma aplicação de MIDP (MIDlet) que possui um ciclo de vida, biblioteca Gráfica
e controle de eventos (classes javax.microedition.lcdui.*).

4.3. Verificação de Classe

A especificação CLDC exige que todas as classes passem por um processo de verificação de duas
fases. A primeira verificação (pré-verificação) deverá está terminada antes da instalação no
dispositivo. A segunda verificação ocorre no dispositivo durante a execução, realizada pela
Máquina Virtual Java (JVM).

Desenvolvimento de Aplicações Móveis 9


JEDITM

File.java
Instalação

compilar (javac) verificar


(execução)

File.class

pré-verificar interpretar

File.class

Máquina de
Desenvolvimento Dispositivo

Figura 3: Processo de Verificação em duas fases

4.4. O Framework Genérico de Conexão (GCF)

O Framework Genérico de Conexão fornece as APIs básicas para conexão em CLDC. Este
framework fornece uma base comum para conexões como HTTP, Sockets e Datagramas. O GCF
fornece um conjunto genérico e comum de APIs que abstraem todos os tipos de conexão. Note-se
que nem todos os tipos de conexões são exigidas para serem implementados em dispositivos
MIDP.
A hierarquia de interface extensível do GCF torna a generalização possível. Novos tipos de
conexões podem ser adicionados neste framework através de extensões desta hierarquia.

Connection

StreamConnectionNotifier DatagramConnection

InputConnection OutputConnection

StreamConnection

ContentConnection

Figura 4: A Hierarquia de Conexão GCF

Desenvolvimento de Aplicações Móveis 10


JEDITM

5. CDC
A Configuração de Dispositivo Conectada (CDC - Connected Device Configuration) é um super-
conjunto da CLDC. Ela provê um ambiente de execução Java mais amplo que o da CLDC e é um
ambiente mais próximo do da JSE.

Figura 5: Visão da CDC

A Máquina Virtual Java CDC (ou CVM) é uma Máquina Virtual Java completa. A CDC contém todas
as APIs da CLDC. Ela provê um subconjunto maior das classes da JSE.
Como a CLDC, a CDC não define nenhuma classe de interface com o usuário. As bibliotecas de
interface com o usuário são definidas pelos perfis desta configuração.
As classes incluídas na CDC vêm dos seguintes pacotes:
• java.io
• java.lang
• java.lang.ref
• java.lang.math
• java.net
• java.security
• java.security.cert
• java.text
• java.util
• java.util.jar
• java.util.zip
CDC também inclui o Framework de Conexão Genérica. Ela requer tipos de conexão adicionais
como suporte para arquivo e datagrama.

Desenvolvimento de Aplicações Móveis 11


JEDITM

6. JWTI
A Tecnologia Java Para a Indústria Sem Fio (JWTI - Java Technology for the Wireless Industry)
especifica um conjunto de serviços e especificações padrão. De acordo com a especificação JWTI,
seu principal objetivo é "minimizar a fragmentação de APIs no mercado de telefones celulares, e
entregar uma especificação clara e previsível para os fabricantes de dispositivos, operadores e
desenvolvedores de aplicação".
Por atenderem à JWTI, as aplicações rodarão em um conjunto maior de dispositivos. Os
fabricantes de dispositivos irão se beneficiar também porque um conjunto maior de aplicações
estará disponíveis para seus dispositivos.

MIDlets

WMA MMAPI Pacotes OEM


MIDP 2.0
1.1 1.1 Opcionais APIs

CLDC 1.0 or 1.1

Sistema Operacional do Dispositivo

Figura 6: Componentes JWTI

Desenvolvimento de Aplicações Móveis 12


JEDITM

7. MIDP
O Perfil de Dispositivo de Informação Móvel (MIDP - Mobile Information Device Profile) é contruído
sobre a CLDC. Não se deve escrever aplicações móveis úteis apenas usando as APIs CLDC. É na
MIDP que as APIs de interface com o usuário são definidas.
A especificação MIDP, assim como a CLDC e outras APIs, foi definida através do Java Community
Process (JCP). Foi envolvido um grupo de profissionais de mais de 50 empresas, composta de
fabricantes de dispositivos móveis, operadoras e desenvolvedores de software. A MIDP está
continuamente evoluindo, com futuras versões passando pelo mesmo rigor do processo do JCP.
Versões futuras do MIDP terão compatibilidade com as versões anteriores, como no caso do
MIDP1 e MIDP 2.0.
A especificação MIDP define que um dispositivo MID deve ter as seguintes características, no
mínimo:
• Visor:
• Tamanho da Tela: 96x54
• Profundidade do Visor: 1-bit
• Pixel aspect ratio: aproximadamente 1:1

• Entrada:
• Um ou mais dos seguintes mecanismos de entrada: teclado de uma mão, teclado de duas
mãos ou tela de toque

• Memória:
• 256 kilobytes de memória não volátil para a implementação MIDP, mais o que for requerido
pela CLDC
• 8 kilobytes de memória não volátil para os dados persistentes criados pela aplicação
• 128 kilobytes de memória volátil para o ambiente Java (ex. Java heap)

• Rede:
• Sem fio, duas vias, possivelmente intermitente, com largura de banda ilimitada

• Som:
• A habilidade de tocar sons, via hardware dedicado ou via software
MIDP define o modelo de aplicação, a API de interface com o usuário, o armazenamento
persistente e a rede, API de mídia e jogos, políticas de segurança, entrega da aplicação e
provisionamento over-the-air.

Desenvolvimento de Aplicações Móveis 13


JEDITM

8. MIDlet
Uma aplicação MIDP é chamada de MIDlet. O software de gerenciamento da aplicação (AMS -
Application Management Software) do dispositivo interage diretamente com o MIDlet com os
métodos de criar, iniciar, pausar e destruir o MIDlet.
O MIDlet é parte do pacote javax.microedition.midlet. Necessita estender a classe MIDlet. E pode
requisitar parâmetros do AMS conforme definido no descritor da aplicação (JAD – Java Application
Descriptor).
Um MIDlet não utiliza o método public static void main(String[] args). Caso possua, este não será
reconhecido pelo AMS como o ponto de início do programa.

8.1. Ciclo de Vida do MIDlet

A vida de um MIDlet começa quando ele é instanciado pelo AMS. Ele inicialmente entra no estado
pausado após ser criado com comando new. O AMS chama o construtor público sem argumento
do MIDlet. Se uma exceção ocorrer no construtor, o MIDlet é colocado no estado destruído e é
descartado imediatamente.
O MIDlet entra no estado ativo depois de se chamar o método startApp() pelo AMS.
O MIDlet entra no estado destruído quando o AMS chama o método destroyApp(). Este estado
também é atingido quando o método notifyDestroyed() retorna com sucesso para a aplicação.
Observe que o MIDlet entra no estado destruído somente uma vez no seu tempo de vida.

new

destroyApp()

startApp() Pausado

Destruído
Ativo pauseApp()

destroyApp()

Figura 7: Ciclo de Vida do MIDlet

8.2 MIDlet Suites


As aplicações de MIDP são empacotadas e entregues aos dispositivos como MIDlet suites. Um
MIDlet suite consiste em um Arquivo Java (JAR) e, opcionalmente, um descritor de aplicação Java
(JAD).
Um arquivo JAD é um arquivo texto contendo um conjunto de atributos, alguns dos quais são
requeridos.

Desenvolvimento de Aplicações Móveis 14


JEDITM

9. Exercícios
9.1. Quais são as vantagens do uso de Java como plataforma de
desenvolvimento e execução para os dispositivos móveis?

• aplicações altamente portáteis


• interfaces ricas, bem definidas para o dispositivo
• espaço de memória baixa (KVM)
• ambiente execução seguro
• aplicações dinâmicas (podem carregar aplicações para um dispositivo)

9.2. O que o motivaria para escrever para programas para os


dispositivos móveis?

• o desafio de escrever aplicações otimizadas


• novos conhecimentos
• fator diversão

Desenvolvimento de Aplicações Móveis 15


Módulo 5
Desenvolvimento de Aplicações Móveis

Lição 2
Como Começar

Versão 1.0 - Set/2007


JEDITM

1. Objetivos
Nesta lição, escreveremos e construiremos aplicações Java ME utilizando o emulador e
empacotador de aplicações. A IDE que utilizaremos será o NetBeans 5.5 (www.netbeans.org) com
o pacote adicional denominado NetBeans Mobility Pack 5.5 (pode ser obtido no mesmo site).
Uma IDE (Integrated Development Environment) é um ambiente de programação com um
construtor de GUI, um editor de código ou texto, um compilador e/ou um interpretador e um
depurador. No nosso caso, o NetBeans vem também com um emulador de dispositivos. Isto
permite ver como o programa parecerá num dispositivo real.

Ao final desta lição, o estudante será capaz de:


• Entender a estrutura de um MIDlet
• Criar um projeto Mobile no NetBeans
• Criar e executar um MIDlet no NetBeans

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. "Hello, world!" MIDlet


Vimos na lição anterior o ciclo de vida de um MIDlet. A vida de um MIDlet inicia quando ele é
criado pelo Application Management System (AMS) do dispositivo. Inicialmente, ele fica no estado
"pausado".

new

destroyApp()

startApp() Pausado

Destruído
Ativo pauseApp()

destroyApp()

Figura 1: Ciclo de Vida do MIDlet

Para ser capaz de criar um MIDlet, devemos criar uma subclasse da classe MIDlet do pacote
javax.microedition.midlet. Deve-se sobrescrever ou implementar os métodos: startApp(),
destroyApp() e pauseApp(). Estes são os métodos esperados pelo AMS para se executar e
controlar o MIDlet.
Diferentemente de um programa Java típico onde o método main() é executado automaticamente
somente uma única vez na vida de um programa, o método startApp() pode ser chamado mais de
uma vez durante o ciclo de vida do MIDlet. Então, não se deve colocar códigos de inicialização
única no método startApp(). Para isso, deve ser criado um construtor para o MIDlet e as
inicializações devem ser feitas nele.
Eis o código de nosso primeiro programa MIDP:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class HelloMidlet extends MIDlet implements CommandListener {


Display display;
Command exitCommand = new Command("Exit", Command.EXIT, 1);
Alert helloAlert;

public HelloMidlet(){
helloAlert = new Alert(
"Hello MIDlet", "Hello, world!",
null, AlertType.INFO
);
helloAlert.setTimeout(Alert.FOREVER);
helloAlert.addCommand(exitCommand);
helloAlert.setCommandListener(this);
}
public void startApp() {
if (display == null){
display = Display.getDisplay(this);
}
display.setCurrent(helloAlert);

Desenvolvimento de Aplicações Móveis 5


JEDITM

}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public void commandAction(Command c, Displayable d){
if (c == exitCommand){
destroyApp(true);
notifyDestroyed(); // Exit
}
}
}

A seguir, dissecaremos o primeiro MIDlet, focando em cada linha de código:


public class HelloMidlet extends MIDlet implements CommandListener {

Como dito anteriormente, é criada uma subclasse de MIDlet para criar nosso programa MIDP.
Nessa linha, foi criada uma subclasse de MIDlet estendendo-a e dando o nome HelloMidlet. Além
disso, implementaremos a interface CommandListener. Veremos sua função mais a frente.
Display display;
Command exitCommand = new Command("Exit", Command.EXIT, 1);
Alert helloAlert;

Estes são os atributos do MIDlet. O objeto Display (existe somente um display associado por
MIDlet) será utilizado para desenhar na tela do aparelho. O objeto exitCommand é um comando
que poderemos inserir na aplicação, por exemplo, para que possamos finalizar o programa. Se
não fosse colocado qualquer comando de saída, não haveria forma de finalizar um MIDlet de
forma normal. Este será montado da seguinte forma:
public HelloMidlet(){
helloAlert = new Alert(
"Hello MIDlet", "Hello, world!",
null, AlertType.INFO
);
helloAlert.setTimeout(Alert.FOREVER);
helloAlert.addCommand(exitCommand);
helloAlert.setCommandListener(this);
}

O construtor inicializa o objeto Alert. A classe Alert será discutida nas próximas lições. O método
addCommand() do objeto Alert mostra um comando "Exit" na tela. O método
setCommandListener() avisa o sistema para passar todos os eventos de comando para o MIDlet.
public class HelloMidlet extends MIDlet implements CommandListener {

O código "implements CommandListener" serve para controlar o pressionamento das teclas e


comandos, de forma que o programa seja capaz de manipular eventos de "command". Se a classe
implementar a interface CommandListener, deve ser criado o método commandAction(), como se
segue:
public void commandAction(Command c, Displayable d){
if (c == exitCommand){
destroyApp(true);
notifyDestroyed(); // Exit
}
}

Usamos o commandAction() somente para as requisições de saída. Finalizamos a nossa classe


utilizando o método notifyDestroyed() se o comando “Exit” for enviado.
public void startApp() {
if (display == null){
display = Display.getDisplay(this);
}
display.setCurrent(helloAlert);

Desenvolvimento de Aplicações Móveis 6


JEDITM

Este é o ponto de entrada da nossa classe, uma vez que está pronta para ser iniciada pelo AMS.
Lembre-se que o método startApp() pode ser incluído mais de uma vez no ciclo de vida do
MIDlet. Se um MIDlet é pausado (paused), por exemplo, quando ocorre uma chamada telefônica,
neste momento entramos em estado de pausa (pauseApp). Após a chamada o AMS pode retornar
o nosso programa e executar novamente o método startApp(). O método estático setCurrent(), da
classe Display, informa ao sistema que queremos que o objeto alerta seja mostrado na tela.
Podemos iniciar o objeto da exibição chamando o método estático getDisplay() da mesma classe.
O Netbeans cria automaticamente um Java Application Descriptor (JAD) para o nosso projeto. E
insere o arquivo JAD na pasta "dist" localizada na pasta do projeto. Este é um exemplo de arquivo
JAD criado pelo Netbeans:
MIDlet-1: HelloMidlet, , HelloMidlet
MIDlet-Jar-Size: 1415
MIDlet-Jar-URL: ProjectHello.jar
MIDlet-Name: ProjectHello
MIDlet-Vendor: Vendor
MIDlet-Version: 1.0
MicroEdition-Configuration: CLDC-1.1
MicroEdition-Profile: MIDP-2.0

Agora estamos prontos para compilar e rodar o nosso primeiro MIDlet.

Desenvolvimento de Aplicações Móveis 7


JEDITM

3. Utilizando Netbeans e o Mobility Pack


O pré-requisito para esta lição, é o Netbeans 5.5 e o Mobility Pack que devem ser instalados em
seu computador. Ambos são arquivos executáveis bastando ao aluno seguir as telas guias para
efetuar corretamente a instalação.
Passo 1: Iniciar criando um novo Projeto (Opção File | New Project...).

Figura 2: Menu File do NetBeans

Passo 2: Selecionar a Categoria "Mobile"

Figura 3: Janela New Project – Selecionando a categoria

Desenvolvimento de Aplicações Móveis 8


JEDITM

Passo 3: Selecionar "Mobile Application" e pressionar o botão Next

Figura 4: Janela New Project – Selecionando o tipo de projeto

Passo 4:Nomear o projeto e especificar sua localização


Desmarcar a opção "Create Hello MIDlet", criaremos nosso MIDlet em próximas lições

Figura 5: Janela New Project – Selecionando o nome e localização do projeto

Desenvolvimento de Aplicações Móveis 9


JEDITM

Passo 5: Selecionar a configuração do projeto (opcional)

Figura 6: Janela New Project – Selecionando a configuração do projeto

Etapa 6: Criar um novo MIDlet

Figura 7: Menu File do NetBeans

Desenvolvimento de Aplicações Móveis 10


JEDITM

Etapa 7: Selecionar na opção Categories a opção MIDP e em File Types a opção MIDlet

Figura 8: Janela New File – Selecionando o tipo do arquivo

Etapa 8: Informar o nome do MIDlet (HelloMidlet) e pressionar o botão Finish

Figura 9: Janela New Project – Selecionando o nome e localização do projeto

Desenvolvimento de Aplicações Móveis 11


JEDITM

Passo 9: Modificar o código do programa conforme descrito anteriormente

Figura 10: Janela New Project – Selecionando o nome e localização do projeto

Passo 10: Compilar e Executar o MIDlet de maneira idêntica à vista nos projetos anteriores

Figura 11: Janela New Project – Selecionando o nome e localização do projeto

Desenvolvimento de Aplicações Móveis 12


JEDITM

Passo 11: Testar o MIDlet no Emulador

Figura 12: Janelas do Emulador da Aplicação

Desenvolvimento de Aplicações Móveis 13


JEDITM

4. Exercícios
4.1. Múltiplos MIDlets em uma suite MIDlet
Adicionar um novo MIDlet ao projeto ProjectHello. Observar que o Netbeans adiciona
automaticamente um novo MIDlet no arquivo de aplicações JAD quando é utilizado o auxílio "New
File..."

Desenvolvimento de Aplicações Móveis 14


Módulo 5
Desenvolvimento de Aplicações Móveis

Lição 3
Interface de Alto Nível para o Usuário

Versão 1.0 - Set/2007


JEDITM

1. Objetivos
A interface de usuário MIDP foi desenhada para aparelhos portáteis. Aplicações MIDP são
mostradas no limite de área da tela. Dispositivos de memória é o fator pelo qual os aparelhos
portáteis possuem pouca memória.
Com os diferentes tipos de aparelhos móveis, dos mais variados modelos de telefones móveis aos
PDAs, a interfase de usuário MIDP foi desenhada para ser flexível o bastante para ser utilizável
em todos estes aparelhos.
Ao final desta lição, o estudante será capaz de:
• Conhecer as vantagens e desvantagens de se utilizar interfaces gráficas em alto e baixo
nível
• Projetar MIDlets usando componentes de interface gráfica de alto nível
• Identificar as diferentes subclasses de tela
• Saber que diferentes itens podem ser utilizados na forma de objeto

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. Interface de usuário MIDP


MIDP possui classes que podem prover interfaces de usuário com altos e baixos níveis de funções.
Interfaces gráficas de alto nível são desenhadas para serem flexíveis. A aparência desses
componentes não está definida nas especificações. A aparência varia de aparelho para aparelho,
mas o programador pode ficar seguro de que o comportamento de alto nível dos componentes
será o mesmo em todas as especificações e implementações.

Interfaces Gráficas de Alto Nível Interfaces Gráficas de Baixo Nível


Altamente portátil entre aparelhos Pode ser específico do dispositivo
Aparência é a mesma nos aparelhos Aplicação com aparência específica
Navegação do tipo "rolagem" é encapsulada Tem que implementar suas próprias formas
de navegação
Não podem definir a aparência real Definem a aparência em nível de pixel
Não são acessadas por aparelhos com O acesso ao baixo nível de entrada é por
características específicas intermédio do pressionamento de teclas
Figura 1: Comparação entre alto nível e baixo nível

2.1. Display
A principal parte da interface de usuário MIDP é a classe Display. Existe uma e apenas uma
instância do Display para cada MIDlet. MIDlet pode pegar a referência do objeto Display utilizando
o método estático getDisplay() da classe Display, e enviar a referência de um objeto do MIDlet.
MIDlet garante que o objeto Display não será modificado enquanto existir uma instância de
MIDlet. Isso significa que o objeto retornado ao executamos o método getDisplay() é o mesmo
não importando se essa chamada foi realizada nos métodos startApp() ou destroyApp() (veja
mais sobre o ciclo de vida do Midlet).

2.2. Displayable
Apenas um componente Displayable pode ser mostrado de cada vez. Por padrão, Displayable não
é mostrado no visor. Pode ser visível por intermédio da chamada do método setCurrent(). Este
método pode ser chamado quando a aplicação se inicia, caso contrário, uma tela escura será
mostrada ou a aplicação não se iniciará.

new

destroyApp()

startApp() Pausado

Destruído
Ativo pauseApp()

destroyApp()

Figura 2: Ciclo de Vida do MIDlet

Desenvolvimento de Aplicações Móveis 5


JEDITM

O método startApp() da classe MIDlet é o local onde é possível inserir a chamada do método
setCurrent(). Entretanto é necessário levar em consideração que o método startApp() pode ser
chamado mais de uma vez durante a existência do MIDlet. Quando um MIDLet entra em pausa
por intermédio do método pauseApp(), pode ser que tenha ocorrido uma chamada telefônica para
atender. O método startApp() pode ser chamado novamente (após o finalização da chamada
telefônica), assim como pela chamada do método setCurrent() dentro do método startApp().
Iremos obscurecer a tela que estava sendo mostrada antes do aplicativo entrar em pausa (ou
seja, antes da chamada telefônica).
O componente Displayable pode ter um título, um conjunto de comandos, um commandListener e
um Relógio.

Figura 3: Propriedades de Displayable

2.3. Title
O componente Displayable possui um título associado. A posição e aparência deste título é
específica para cada tipo de aparelho e só pode ser determinada quando a aplicação estiver
rodando. O título é associado ao Displayable por intermédio da chamada do método setTitle().
Chamando este método, o título do componente Displayable poderá mudar imediatamente. Se
um Displayable está sendo apresentado na tela corrente, a especificação de estado do MIDP e o
título devem ser alterados pela implementação “tão logo quanto seja permitido fazê-la”.
Passando um parâmetro nulo (null) para o método setTitle() remove-se o título do componente
Displayable. Mudando ou removendo o título, pode-se afetar o tamanho da área disponível. Se a
troca na área do visor ocorrer, o MIDlet pode ser modificada por intermédio do método
sizeChanged().

2.4. Comando
Devido ao limite do tamanho da tela, MIDP não define uma barra de menu dedicada. No lugar da
barra de menu MIDlets possuem comandos. Comandos são usualmente implementados pelo MIDP
que podem ser chaves rápidas ou itens de menu. O objeto da classe Command contém
informações somente sobre as ações que foram capturadas quando ativado, e não códigos que
serão executados quando for selecionado.
A propriedade commandListener do componente Displayable contém as ações que serão
executadas na ativação dos comandos. A commandListener é a interface que especifica um
método simples:
public void commandAction(Command comando, Displayable mostravel)

O mapeamento dos comandos no aparelho depende do número de botões rápidos ou botões


programáveis disponíveis no aparelho. Se o número de comandos não se ajustar ao número de
botões rápidos (softbuttons), o aparelho pode colocar alguns ou todos os comandos dentro do
menu e mapear este menu para os botões rápidos com o título.

Desenvolvimento de Aplicações Móveis 6


JEDITM

Command exitCommand = new Command("Exit", Command.EXIT, 1);


Command newCommand = new Command("New Item", Command.OK, 1);
Command renameCommand = new Command("Rename Item", Command.OK, 1);
Command deleteCommand = new Command("Delete Item", Command.OK, 1);
...
list.addCommand(exitCommand);
list.addCommand(newCommand);
list.addCommand(renameCommand);
list.addCommand(deleteCommand);

Figura 4: Amostra de mapeamento para múltiplos comandos

Um Command possui um label curto, um label longo e opcional, um tipo e uma prioridade.

2.4.1. Label
O tamanho reduzido da tela dos dispositivos é sempre um fator relevante quando se desenvolvem
aplicações MIDP. Para os labels de comando, essa suposição também é aplicável. Os labels devem
ser curtos, porém descritivos, para tanto devem caber na tela e serem compreensíveis para o
usuário final.
Quando um label longo for especificado, deverá ser apresentado toda vez que o sistema
considerar apropriado. Não existe uma chamada de API que especifique qual label deverá ser
mostrado. É perfeitamente possível que alguns comandos apresentem um label curto enquanto
que outros apresentem um longo.

2.4.2. Tipo de Comando


A maneira pela qual um comando é apresentado depende do dispositivo utilizado. Um
programador pode especificar o tipo para este comando. Este tipo servirá como um auxílio para
saber onde o comando deverá ser colocado. Não existe nenhuma forma de definir perfeitamente
onde o comando deve ser apresentado na tela.
Os diferentes tipos de comandos são:
Command.OK, Command.BACK,
Command.CANCEL, Command.EXIT,
Command.HELP, Command.ITEM,
Command.SCREEN, Command.STOP

Desenvolvimento de Aplicações Móveis 7


JEDITM

Figura 5: A apresentação dos comandos é diferente em cada aparelho.

2.4.3. Prioridade de Comandos


A aplicação pode especificar a importância dos comandos na propriedade priority (prioridade).
Esta é uma propriedade com um argumento do tipo Integer e quanto menor o número, maior a
prioridade. Esta propriedade é também uma dica para ajudar a saber como o comando deverá ser
posicionado. Casualmente a implementação determina a posição dos comandos pelo seu tipo. Se
existir mais de um comando do mesmo tipo, a prioridade é normalmente considerada no
posicionamento dos comandos.

2.5. CommandListener
O CommandListener é uma interface que possui um único método:
void commandAction(Command command, Displayable displayable)

O método commandAction() é chamado quando um comando é selecionado. A variável do


comando é uma referência para o comando que foi selecionado. Displayable é onde o comando
está localizado e onde a ação selecionada acontece.
O método commandAction deverá retornar imediatamente, caso contrário a execução da aplicação
poderá ser bloqueada. Isto se deve pelo fato das especificações do MIDP não requisitarem
implementações para a criar uma tarefa separada para cada evento entregue.

2.6. Ticker
O Ticker é uma contínua linha de rolagem de texto que acompanha a tela. O método construtor
do Ticker aceita uma String para ser exibida. Ele só possui dois outros métodos, o padrão get e
set para este texto: String getString() e void setString(String texto). Não existe possibilidade da
aplicação controlar a velocidade e a direção do texto. A rolagem não pode ser pausada ou
interrompida.
Se uma quebra de linha for embutida no texto, está não será apresentada na tela. Todas as linhas
de texto deverão ser exibidas como uma única linha de texto rolante.
Um objeto Ticker é anexado a um dispositivo de exibição chamando-se o método setTicker().
Caso já exista um objeto Ticker anexado ao dispositivo, este será substituído pelo novo objeto
passado como parâmetro.
Passando-se um parâmetro null para o método setTicker() ocorre a remoção qualquer objeto
Ticker anexado do dispositivo. A remoção de um objeto Ticker do dispositivo pode afetar o
tamanho de área válido para o conteúdo a ser exibido. Se uma mudança no tamanho da área de

Desenvolvimento de Aplicações Móveis 8


JEDITM

exibição ocorrer, o MIDlet deverá ser notificado pelo método sizeChanged().


Os objetos Displayable podem compartilhar uma instância do objeto Ticker.

2.7. Screen
A classe Screen é a principal classe abstrata utilizada para aplicações gráficas de alto nível
enquanto que a classe Canvas é a principal classe abstrata para aplicações gráficas de baixo nível.
Existem quatro subclasses da classe abstrata Screen: Form, TextBox, List e Alert.

Figura 6: Hierarquia de classes

2.8. Item
Componentes do tipo Item podem ser colocados em um componente do tipo container, como um
Form ou um Alert. Um item pode possuir as seguintes propriedades:

Propriedade Valor Padrão


Label Especificado no construtor da subclasse
Commands nenhum
defaultCommand nulo
ItemCommandListener nulo
Layout directive LAYOUT_DEFAULT
Preferências de altura e largura -1 (destravado)

Figura 7: Hierarquia de Classe Item

Desenvolvimento de Aplicações Móveis 9


JEDITM

A diretiva Layout especifica o formato de um item dentro de um objeto do tipo Form. A diretiva
Layout pode ser combinada usando o operador de comparação binária OR (|). Todavia, diretivas
de mesma orientação são mutuamente exclusiva.
Estas são as diretivas de alinhamento horizontal mutuamente exclusivas:
LAYOUT_LEFT
LAYOUT_RIGHT
LAYOUT_CENTER

Estas são as diretivas de alinhamento vertical mutuamente exclusivas:


LAYOUT_TOP
LAYOUT_BOTTOM
LAYOUT_VCENTER

Outras diretivas de layout não mutuamente exclusiva são:


LAYOUT_NEWLINE_BEFORE
LAYOUT_NEWLINE_AFTER
LAYOUT_SHRINK
LAYOUT_VSHRINK
LAYOUT_EXPAND
LAYOUT_VEXPAND
LAYOUT_2

Desenvolvimento de Aplicações Móveis 10


JEDITM

3. Alert
A classe Alert gera uma tela na qual é possível exibir um texto e uma imagem. É um componente
para exibir erro, aviso, texto e imagem informativa ou trazer uma tela de confirmação para o
usuário.
É exibido por um determinado período de tempo. Este tempo pode ser fixado utilizando-se o
método setTimeout() e especificado em milissegundos. Pode ser construído para ser exibido até
que o usuário ative um comando Done dentro do intervalo de tempo de espera especificado pela
contstante Alert.FOREVER.
O Alert pode também exibir um componente do tipo Gauge como um indicador. Quando um Alert
conter um texto esse não ajustará a tela inteira e deverá ser rolado. Alert é fixado
automaticamente para modal (tempo de espera fixado para Alert.FOREVER).
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class AlertMidlet extends MIDlet implements CommandListener {


Display display;
Form mainForm;
Command exitCommand = new Command("Exit", Command.EXIT, 0);
Command okCommand = new Command("Ok", Command.OK, 0);
Gauge gauge = new Gauge(null, false, 5, 0);
Command[] commands = {
new Command("Alarm", Command.OK, 0),
new Command("Confirmation", Command.OK, 0),
new Command("Info", Command.OK, 0),
new Command("Warning", Command.OK, 0),
new Command("Error", Command.OK, 0),
new Command("Modal", Command.OK, 0)
};

Alert[] alerts = {
new Alert("Alarm Alert",
"Example of an Alarm type of Alert",
null, AlertType.ALARM),
new Alert("Confirmation Alert",
"Example of an CONFIRMATION type of Alert",
null, AlertType.CONFIRMATION),
new Alert("Info Alert",
"Example of an INFO type of Alert",
null, AlertType.INFO),
new Alert("Warning Alert",
"Example of an WARNING type of Alert, w/ gauge indicator",
null, AlertType.WARNING),
new Alert("Error Alert",
"Example of an ERROR type of Alert, w/ an 'OK' Command",
null, AlertType.ERROR),
new Alert("Modal Alert",
"Example of an modal Alert: timeout = FOREVER",
null, AlertType.ERROR),
};
public AlertMidlet(){
mainForm = new Form("JEDI: Alert Example");

mainForm.addCommand(exitCommand);
for (int i=0; i< commands.length; i++){
mainForm.addCommand(commands[i]);
}
mainForm.setCommandListener(this);
// Adiciona um objeto Gauge e envia o tempo limite
alerts[3].setIndicator(gauge);
alerts[3].setTimeout(5000);
// Adiciona um comando para este Alert

Desenvolvimento de Aplicações Móveis 11


JEDITM

alerts[4].addCommand(okCommand);
// Define o Alert como Modal
alerts[5].setTimeout(Alert.FOREVER);
}

public void startApp() {


if (display == null){
display = Display.getDisplay(this);
display.setCurrent(mainForm);
}
}

public void pauseApp() {}

public void destroyApp(boolean unconditional) {}

public void commandAction(Command c, Displayable d){


if (c == exitCommand){
destroyApp(true);
notifyDestroyed(); // Exit
}
for (int i=0; i<commands.length; i++){
if (c == commands[i]){
display.setCurrent(alerts[i]);
}
}
}
}

INFO Alert Alert Modal Alert com o indicador gauge


Figura 8: Diferentes tipos de Alert

Desenvolvimento de Aplicações Móveis 12


JEDITM

4. List
A classe List é uma subclasse de Screen e fornece uma lista de escolhas. Este objeto pode
assumir três modelos: IMPLICIT, EXCLUSIVE ou MULTIPLE.
List é IMPLICIT e o usuário executar o botão "select", o método commandAction() da classe List
será chamada. O comando padrão é List.SELECT_COMMAND. O comando commandListener pode
ser chamado através do comando padrão List.SELECT_COMMAND.
O método getSelectedIndex() retorna o índice do elemento atualmente selecionado para os tipos
IMPLICIT e EXCLUSIVE. Para o tipo MULTIPLE, o método getSelectedFlags() retorna um atributo
do tipo boolean contendo o estado dos elementos. O método isSelected(int index) retorna o
estado do elemento na posição de índice dada.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ListMidlet extends MIDlet implements CommandListener {


Display display;
List list;
Command exitCommand = new Command("Exit", Command.EXIT, 1);
Command newCommand = new Command("New Item", Command.OK, 1);
Command renameCommand = new Command("Rename Item", Command.OK, 1);
Command deleteCommand = new Command("Delete Item", Command.OK, 1);
Ticker ticker = new Ticker(
"JEDI - Java Education and Development Initiative");

public ListMidlet(){
list = new List("JEDI: List Example", List.IMPLICIT);
list.append("List Item #1", null);
list.append("List Item #2", null);
list.append("List Item #3", null);
list.setTicker(ticker);
list.addCommand(exitCommand);
list.addCommand(newCommand);
list.addCommand(renameCommand);
list.addCommand(deleteCommand);
list.setCommandListener(this);
}

public void startApp() {


if (display == null){
display = Display.getDisplay(this);
display.setCurrent(list);
}
}

public void pauseApp() {


}

public void destroyApp(boolean unconditional) {


}

public void commandAction(Command c, Displayable d){


if (c == exitCommand){
destroyApp(true);
notifyDestroyed(); // Exit
}
if (c == List.SELECT_COMMAND){
int index = list.getSelectedIndex();
String currentItem = list.getString(index);
// realiza algo
}
}
}

Desenvolvimento de Aplicações Móveis 13


JEDITM

Lista IMPLICIT Lista EXCLUSIVE Lista MULTIPLE


Figura 9: Tipos de lista

Desenvolvimento de Aplicações Móveis 14


JEDITM

5. TextBox
A classe TextBox é a subclasse de Screen que pode ser usada para se obter a entrada de texto do
usuário. Permite que o usuário incorpore e edite o texto. É similar à classe TextField (ver o item
sobre TextField) pois permite a entrada de constraints e de modalidades. Sua diferença em
relação a classe TextField é que o usuário pode inserir uma nova linha (quando a constraint da
entrada é informada).
O texto digitado no objeto TextBox pode ser recuperado utilizando o método getString().

Figura 10: TextBox com múltiplas linhas

Figura 11: TextBox com o PASSWORD modificado

Desenvolvimento de Aplicações Móveis 15


JEDITM

6. Form
A classe Form é uma subclasse da Screen. É um contêiner para itens das subclasses, tais como os
objetos das classes TextField, StringItem, ImageItem, DateField e ChoiceGroup. Controla a
disposição dos componentes e a transversal entre os componentes e o desdobramento da tela.
Itens são adicionados e inseridos a um objeto do tipo Form usando os métodos append() e
insert(), respectivamente. Itens são eliminados usando o método delete(). Itens podem ser
substituídos usando o método set(). Itens são referenciados usando o índice de base zero.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class MidletPrinc extends MIDlet implements CommandListener {

private Command exitCmd = new Command("Exit", Command.EXIT, 0);


private ScreenForm form;
private Display display;

public void startApp() {


if (display == null) {
form = new ScreenForm("Test Form");
display = Display.getDisplay(this);
}
display.setCurrent(form);
}
public void commandAction(Command command, Displayable displayable) {
if (command == exitCmd) {
try {
destroyApp(true);
} catch (MIDletStateChangeException ex) {
ex.printStackTrace();
}
notifyDestroyed();
}
}
protected void pauseApp() {
}
protected void destroyApp(boolean b) throws MIDletStateChangeException {
}
class ScreenForm extends Form {
public ScreenForm(String title) {
super(title);
addCommand(exitCmd);
setCommandListener(MidletPrinc.this);

// Instruções para o Form


}
}
}

Neste MIDlet observe o comentário “Instruções para o Form”. Este servirá como ponto de entrada
para os próximos exemplos, que deverão serem inseridos exatamente a partir deste ponto.

Desenvolvimento de Aplicações Móveis 16


JEDITM

7. ChoiceGroup
Um componente ChoiceGroup representa grupos de escolhas selecionadas. A escolha pode conter
um texto, uma imagem ou ambas.
As escolhas podem ser EXCLUSIVE (somente uma opção pode ser selecionada) ou MULTIPLE
(várias opções podem ser selecionadas de uma vez). Caso um objeto do tipo ChoiceGroup seja
um tipo de POPUP, somente uma opção poderá ser selecionada. Uma seleção de popup será
exibida quando este item for selecionado. Cabe ao usuário efetuar uma única escolha. A opção
exibida é sempre a seleção escolhida.
O método getSelectedIndex() retorna o índice do elemento selecionado de um ChoiceGroup. O
método getSelectedFlags() retorna um grupo de atributos do tipo boolean que corresponde ao
estado de cada um dos elementos. O método isSelected(int index) retorna o estado de um
elemento a partir da posição informada no atributo index.
// Insira estas instruções no Form
ChoiceGroup choiceExclusive = new ChoiceGroup("Exclusive", Choice.EXCLUSIVE);
choiceExclusive.append("Male", null);
choiceExclusive.append("Female", null);
append(choiceExclusive);

ChoiceGroup choiceMultiple = new ChoiceGroup("Multiple", Choice.MULTIPLE);


choiceMultiple.append("Apple", null);
choiceMultiple.append("Orange", null);
choiceMultiple.append("Grapes", null);
append(choiceMultiple);

ChoiceGroup choicePopup = new ChoiceGroup("Popup", Choice.POPUP);


choicePopup.append("Asia", null);
choicePopup.append("Europe", null);
choicePopup.append("Americas", null);
append(choicePopup);

Figura 12: Modelos de Grupos de Escolha

Desenvolvimento de Aplicações Móveis 17


JEDITM

8. DateField
O componente DateField é utilizado para as entradas de data e hora. Pode conter uma entrada de
data (modo DATE), uma entrada de hora (modo TIME) ou ambas (modo DATE_TIME).
O método getDate() retorna o valor atual de um item e retornará null caso este item não seja
inicializado. Caso o modo do DataField seja DATE, a hora do componente irá retornar zero. Se o
modo for TIME, a data do componente é definido para "Janeiro 1, 1970".
// Insira estas instruções no Form
DateField dateonly = new DateField("Birthday (DATE)", DateField.DATE);
DateField timeonly = new DateField("Set Alarm (TIME)", DateField.TIME);
DateField datetime =
new DateField("Departure (DATE_TIME)", DateField.DATE_TIME);

append(dateonly);
append(timeonly);
append(datetime);

Entrada de DateField Selecionando Datas Selecionando Horas


Figura 13: Modelos de DateField e telas de entrada

Desenvolvimento de Aplicações Móveis 18


JEDITM

9. StringItem
Um componente StringItem é um componente somente de leitura. Sendo composto de um label e
um texto.
Um objeto do tipo StringItem, opcionalmente, permite um argumento que representa a
aparência. O modo de aparência pode ser definido através das constantes Item.PLAIN,
Item.HYPERLINK ou Item.BUTTON.
Caso o modo da aparência seja do tipo HYPERLINK ou BUTTON, o comando padrão e o
ItemCommandListener precisam ser definidos no item.
// Insira estas instruções no Form
StringItem plain = new StringItem("Plain", "Plain Text", Item.PLAIN);
StringItem hyperlink =
new StringItem("Hyperlink", "http://www.sun.com", Item.HYPERLINK);
hyperlink.setDefaultCommand(new Command("Set", Command.ITEM, 0));
StringItem button = new StringItem("Button", "Click me", Item.BUTTON);
button.setDefaultCommand(new Command("Set", Command.ITEM, 0));
append(plain);
append(hyperlink);
append(button);

Figura 14: StringItem

Desenvolvimento de Aplicações Móveis 19


JEDITM

10. ImageItem
O componente ImageItem é uma imagem gráfica que pode ser colocada em um componente, tal
como um Form. O objeto ImageItem permite um objeto do tipo layout como parâmetro (veja
mais na seção sobre Item):
public ImageItem(
String label,
Image img,
int layout,
String altText)

Outro construtor aceita uma modalidade de aparência, podendo ser um dos seguintes atributos
definidos: Item.PLAIN, Item.HYPERLINK ou Item.BUTTON (ver mais na seção sobre StringItem):
public ImageItem(String label,
Image image,
int layout,
String altText,
int appearanceMode)

O arquivo "jedi.png" é importado para o projeto usando-se a operação de gerenciamento de


arquivos do sistema e inserido no diretório do projeto sobre o subdiretório "src". O projeto é
atualizado pelo clique com botão direito do mouse sobre o nome do projeto e seleção de "Refresh
Folders".
// Insira estas instruções no Form
try {
Image img = Image.createImage("/jedi.png");
ImageItem image = new ImageItem("JEDI", img, Item.LAYOUT_CENTER, "JEDI Logo");
append(image);
} catch (Exception e){
e.printStackTrace();
}

Figura 15: ImageItem

Desenvolvimento de Aplicações Móveis 20


JEDITM

11. TextField
Um componente TextField é um item onde o usuário pode codificar a entrada. Diversas regras de
entrada, mutuamente exclusivas, podem ser ajustadas:
TextField.ANY TextField.EMAILADDR
TextField.NUMERIC TextField.PHONENUMBER
TextField.URL TextField.DECIMAL

A entrada pode também ter estes modificadores:

TextField.PASSWORD TextField.UNEDITABLE
TextField.SENSITIVE TextField.NON_PREDICTIVE
TextField.INITIAL_CAPS_WORD TextField.INITIAL_CAPS_SENTENCE

Estes modificadores podem ser atribuídos usando-se o operador de comparação binária OR (|) (ou
pelo uso do operador de comparação binária: XOR ^) sobre as regras de entrada.
Consequentemente, modificadores podem ser derivados do valor do retorno do método
getConstraints() ao operador de comparação binária AND (&).
Já que os modificadores são retornados pelo método getConstraints(), a entrada principal pode
ser extraída pelo uso do operador de comparação binária com TextBox.CONSTAINT_MASK e o
retorno do valor do método getConstaints().
O método getString() retorna o conteúdo do TextField como um valor do tipo caractere.
// Insira estas instruções no Form
TextField ANY = new TextField("ANY", "", 64, TextField.ANY);
TextField EMAILADDR = new TextField("EMAILADDR", "", 64, TextField.EMAILADDR);
TextField NUMERIC = new TextField("NUMERIC", "", 64, TextField.NUMERIC);
TextField PHONENUMBER =
new TextField("PHONENUMBER", "", 64, TextField.PHONENUMBER);
TextField URL = new TextField("URL", "", 64, TextField.URL);
TextField DECIMAL = new TextField("DECIMAL", "", 64, TextField.DECIMAL);

append(ANY);
append(EMAILADDR);
append(NUMERIC);
append(PHONENUMBER);
append(URL);
append(DECIMAL);

Figura 16: TextField Items

Desenvolvimento de Aplicações Móveis 21


JEDITM

12. Exercícios
12.1. Lista dinâmica
Criar um MIDlet com uma List do tipo IMPLICIT na tela principal. Anexar três comandos para esta
List – "Add Item", "Remove Item" e "Exit". O comando "Add Item" alertará o usuário para uma
entrada a lista usando um TextBox, então adicionará este antes do item selecionado na lista.
"Remove Item" removerá o item selecionado da lista (dica, veja o método getSelectedIndex). O
Comando "Exit" finalizará o Midlet.

Desenvolvimento de Aplicações Móveis 22


Módulo 5
Desenvolvimento de Aplicações Móveis

Lição 4
Interface de Baixo Nível para o Usuário

Versão 1.0 - Set/2007


JEDITM

1. Objetivos
Nas lições anteriores dissertamos sobre como construir interfaces do usuário tais como listas,
formulários e dados de entrada. Aquelas eram interfaces de alto nível e o programador não tinha
que se preocupar sobre como desenhar a tela ou posicionar o texto na tela. Todo programa tem
que especificar apenas o tipo de componentes e o nomes dos elementos. O sistema encarrega-se
de manusear o desenho, a rolagem e o layout da tela.
Uma desvantagem de se utilizar somente componentes de alto nível é que o programa não tem
controle total sobre a tela. Há ocasiões em que queremos desenhar linhas, animar imagens e ter
controle sobre cada ponto da tela.
Nesta lição trabalharemos diretamente com o desenho da tela. Estudaremos a classe Canvas, que
deve ser o fundo de nosso desenho. Trabalharemos também com a classe Graphics que tem
métodos para desenho de linhas, retângulos, arcos e texto. Também falaremos sobre fontes, cores
e imagens.
Ao final desta lição, o estudante será capaz de:
• Compreender os eventos de baixo nível manuseados em MIDP
• Desenhar e exibir texto, imagens, linhas, retângulos e arcos
• Especificar cor, fonte e movimento para operações de desenho
• Compreender e utilizar as classes Canvas e Graphics

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. Canvas
A classe Canvas é uma subclasse de Displayable. Uma classe abstrata que deve ser instanciada
antes que uma aplicação possa fazer uso de suas funcionalidades.

Figura 1: Hierarquia de classes.

Pode ser implementada com a subclasse Screen de exibição. O programa pode alternar para
Canvas e Screen. Define métodos vazios de manuseio. As aplicações devem implementá-los para
manusear os eventos.
A classe define um método abstrato chamado paint(). Aplicações que utilizam esta classe devem
providenciar a implementação deste método.

2.1. O sistema de Coordenadas

O sistema de coordenadas da classe Canvas é baseado no (0,0)


increasing x value
ponto de coordenada zero, ou seja, as coordenadas x e y
iniciam com o valor zero. O canto esquerdo superior é a
coordenada (0,0). A coordenada x é incrementada da
esquerda para a direita, enquanto a coordenada y é
incrementada de cima para baixo. Os métodos getWidth() e
increasingy value

getHeight() retornam a largura e a altura do Canvas,


respectivamente.
O canto inferior direito da tela tem as coordenadas nas
posições getWidht()-1 e getHeight()-1. Quaisquer
alterações no tamanho de desenho do Canvas é registrado
para a aplicação pelo método sizeChange(). O tamanho
disponível do Canvas pode mudar se acontecer uma troca
entre os modos normal e tela cheia ou a adição ou remoção
de componentes tais como Comandos.
(getwidth()-1,getHeight()-1)

Figura 2: O sistema Coordinate

2.2. "Hello, world!"


import javax.microedition.midlet.*;

Desenvolvimento de Aplicações Móveis 5


JEDITM

import javax.microedition.lcdui.*;

public class HelloCanvasMIDlet extends MIDlet {


private Display display;
HelloCanvas canvas;
Command exitCommand = new Command("Exit", Command.EXIT, 0);

public void startApp() {


if (display == null){
canvas = new HelloCanvas(this, "Hello, world!");
display = Display.getDisplay(this);
}
display.setCurrent(canvas);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
protected void Quit(){
destroyApp(true);
notifyDestroyed();
}
}

class HelloCanvas extends Canvas implements CommandListener {


private Command exitCommand = new Command("Exit", Command.EXIT, 0);
private HelloCanvasMIDlet midlet;
private String text;
public HelloCanvas(HelloCanvasMIDlet midlet, String text) {
this.midlet = midlet;
this.text = text;

addCommand(exitCommand);
setCommandListener(this);
}
protected void paint(Graphics g) {
// Limpa a janela
g.setColor(255, 255, 255 );
g.fillRect(0, 0, getWidth(), getHeight());
// Define a cor como preta
g.setColor(0, 0, 0);
// E escreve o texto
g.drawString(text,
getWidth()/2, getHeight()/2,
Graphics.TOP | Graphics.HCENTER);
}
public void commandAction(Command c, Displayable d) {
if (c == exitCommand){
midlet.Quit();
}
}
}

Desenvolvimento de Aplicações Móveis 6


JEDITM

Figura 3: Hello Word utilizando Canvas

Com o midlet “Hello, world!” definimos a classe que estende a classe Canvas
class HelloCanvas extends Canvas implements CommandListener {

Adicionamos um comando “Exit” (saída) e o colocamos como listener do comando


addCommand(exitCommand);
setCommandListener(this);

Criamos um comando de listener através da implementação de uma classe CommandListener.


Isto significa criar uma classe que tenha um método commandAction.
class HelloCanvas extends Canvas implements CommandListener {
public void commandAction(Command c, Displayable d) {
...
}
}

O ponto central deste programa é o método paint(). A primeira chamada deste método limpa a
tela:
g.setColor(255, 255, 255 );
g.fillRect(0, 0, getWidth(), getHeight());

E os métodos gráficos de baixo nível desenham a mensagem “Hello, world!” na tela:


g.setColor(0, 0, 0);
g.drawString(text, getWidth()/2, getHeight()/2,
Graphics.TOP | Graphics.HCENTER);

2.3. Comandos

Assim como as classes List, TextBox e Form, a classe Canvas pode possuir Commands anexados e
pode escutar por eventos de comando. Os passos para adicionar um comando a um Canvas são
os mesmos:
1. Criar um objeto da classe Command
private Command exitCommand = new Command("Exit", Command.EXIT, 0);

2. Utilizar o método addCommand() para anexar o comando ao objeto da classe Canvas (Form,
List ou TextBox)
addCommand(exitCommand);

3. Utilizar o método setCommandListener() para registrar qual classe obtém os eventos de


comando neste Displayable

Desenvolvimento de Aplicações Móveis 7


JEDITM

setCommandListener(this);

4. Criar um commandListener para implementar a classe CommandListener e providenciar o


método commandAction()
class HelloCanvas extends Canvas implements CommandListener {
...
public void commandAction(Command c, Displayable d) {
if (c == exitCommand){
// do something
}
}
}

2.4. Eventos de Teclado

A classe Canvas pode ter uma interface listener para eventos de teclado que podem implementar
os seguintes métodos:
keyPressed(int keyCode) Quando uma tecla é pressionada
keyRepeated(int keyCode) Quando uma tecla é repetida (quando é mantida pressionada)
keyReleased(int keyCode) Quando uma tecla é liberada

A classe Canvas define os seguintes códigos de teclado: KEY_NUM0, KEY_NUM1, KEY_NUM2,


KEY_NUM3, KEY_NUM4, KEY_NUM5, KEY_NUM6, KEY_NUM7, KEY_NUM8, KEY_NUM9, KEY_STAR,
e KEY_POUND.
Para recuperar o nome da tecla deve-se utilizar o método getKeyName(int keyCode).

2.5. Ações de Jogo

Cada código de tecla pode ser mapeada para uma ação de jogo. A classe Canvas define estas
ações: UP, DOWN, LEFT, RIGHT, FIRE, GAME_A, GAME_B, GAME_C, GAME_D. O programa pode
traduzir um código de tecla em uma ação de jogo utilizando o método getGameAction(int
keyCode).
O método getKeyCode(int gameAction) retorna o código de tecla associado com uma ação de
jogo. Uma ação de jogo pode ter mais de um código de teclado associado. Se houver mais de um
código de tecla associado com uma ação de jogo é retornado apenas um único código.
Uma aplicação deve utilizar o método getGameAction(int keyCode) ao invés de usar diretamente
os códigos de teclado. Normalmente, se um programa precisa esperar por uma tecla “UP”, ele
deveria utilizar o atributo KEY_NUM2 ou o código específico para o botão UP. No entanto,
programas que utilizam este método não são portáveis em aparelhos que possam ter diferentes
formatos para o código da tecla do botão UP. O KEY_NUM2 pode ser a “tecla UP” para um
aparelho, entretanto, pode ser a “tecla LEFT” em outro aparelho. O método getGameAction() deve
sempre retornar UP, independente de qual tecla foi pressionada já que é esta que está no
contexto do aparelho.

2.6. Eventos de apontador

Apesar dos eventos de teclado, programas MIDP também podem conter eventos de apontador.
Isto é verdade se o aparelho possuir um apontador e estiver implementado no sistema Java do
aparelho
O método hasPointerEvents() retorna true se o aparelho suportar eventos de pressão e liberar
um apontador. O método hasPointerMotionEvents() retorna true se o aparelho suportar eventos
de movimentação de apontador.
public boolean hasPointerEvents()
public boolean hasPointerMotionEvents()

Os eventos que são gerados por atividade de ponto são definidos pela implementação dos

Desenvolvimento de Aplicações Móveis 8


JEDITM

métodos: pointerPressed, pointerReleased e pointerDragged. Uma aplicação precisa implementar


estes métodos para ser notificada quando ocorrerem. As coordenadas x e y do evento (onde o
apontador foi pressionado, solto ou arrastado) são especificadas como parâmetros nas assinaturas
dos seguintes métodos:
protected void pointerPressed(int x, int y)
protected void pointerReleased(int x, int y)
protected void pointerDragged(int x, int y)

Desenvolvimento de Aplicações Móveis 9


JEDITM

3. Gráficos
A classe Graphics é a principal classe para desenhar textos, imagens, linhas, retângulos e arcos.
Possui métodos para especificar cor, fonte e estilo.

3.1. Cores

A classe Display possui métodos para determinar se o aparelho possui suporte a cor e o número
de cores ou tons de cinza suportados por ele.
public boolean isColor() Retorna true se a tela suportar cor, retorna false se não
public int numColors() Retorna o número de cores (ou tons de cinza se o
aparelho não suportar cores) suportados pelo aparelho

Para definir a cor que será utilizada para os próximos métodos, utilize o método setColor(). Pode
ser utilizado de duas maneiras:
public void setColor(int red, int green, int blue)
public void setColor(int RGB)

Na primeira forma, especifica-se os componentes em padrão RGB, intensidade das cores vermelho
(Red), verde (Green) e azul (Blue) que podem variar de 0 a 255. Na segunda forma, aos
componentes da cor são especificados na forma de 0x00RRGGBB. As chamadas para inteiros no
método setColor seguem o código da mesma maneira:
int red, green, blue;
...
setColor(red, green, blue);
setColor( (red<<16) | (green<<8) | blue );

Outro métodos de manipulação de cores são:


public int getColor() retornar um inteiro da cor atual do formulário no
formato 0x00RRGGBB
public int getRedComponent() retornar o valor da cor vermelho do componente atual
public int getGreenComponent() retornar o valor da cor verde do componente atual
public int getBlueComponent() retornar o valor da cor azul do componente atual
public int getGrayScale() retornar o valor da escala de cinza da cor atual
public void setGrayScale( definir o valor da escala de cinza para uma próxima
int value) instrução

3.2. Fontes

Um objeto do tipo Font possui três atributos: face, style e size. As fontes não são criadas pela
aplicação. Ao invés disso, a aplicação pergunta ao sistema por determinados atributos da fonte e
o sistema retorna uma fonte com estes atributos. O sistema não garante que irá retornar uma
fonte com todos os atributos requeridos. Se o sistema não tem uma fonte igual a que requerida,
retornará uma fonte próxima sem respeitar todos os atributos requeridos.
Font é uma classe separada. Como comentado anteriormente, a aplicação não cria um objeto
padrão. Ao invés disso os métodos estáticos getFont() e getDefaultFont() são utilizados para obter
uma fonte padrão para o sistema.
public static Font getFont( retornar um objeto do tipo Font do sistema com
int face, int style, int size) os atributos definidos
public static Font getDefaultFont() retornar a fonte padrão utilizada pelo sistema
public static Font retornar a fonte utilizada por um componente
getFont(int fontSpecifier) gráfico de auto nível. O atributo fontSpecifier

Desenvolvimento de Aplicações Móveis 10


JEDITM

pode ser:
FONT_INPUT_TEXT ou FONT_STATIC_TEXT

O formato da fonte é especificado pelo parâmetro face que pode assumir uma das seguintes
constantes padrão: FACE_SYSTEM, FACE_MONOSPACE ou FACE_PROPORTIONAL.
O estilo da fonte é especificado pelo parâmetro style que pode assumir uma das seguintes
constantes padrão: STYLE_PLAIN (padrão) ou a combinação de STYLE_BOLD (negrito),
STYLE_ITALIC (itálico) e STYLE_UNDERLINED (sublinhado). Estilos podem ser combinados
utilizando o operador OR (|). O estilo de uma fonte tipo negrito e itálico é declarado como:
STYLE_BOLD| STYLE_ITALIC

O tamanho da fonte é especificado pelo parâmetro size que pode assumir uma das seguintes
constantes padrão: SIZE_SMALL, SIZE_MEDIUM ou SIZE_LARGE.
Estes métodos retornam os atributos específicos da fonte:
public int getStyle()
public int getFace()
public int getSize()
public boolean isPlain()
public boolean isBold()
public boolean isItalic()
public boolean isUnderlined()

3.3. Estilo do Traço

O método setStrokeStyle(int style) especifica o estilo do cursor que deverá ser usado para
desenhar linhas, arcos e retângulos com cantos arredondados. O Estilo do cursor não afeta o
texto, as imagens ou os preenchimentos.
public void setStrokeStyle( definir o estilo do cursor que deverá ser usado para desenhar
int style) linhas, arcos e retângulos com cantos arredondados
public int getStrokeStyle() retornar o estilo atual do cursor

Os valores válidos para o parâmetro style são uma das seguintes constantes padrão: SOLID ou
DOTTED.

3.4. Clipping

Clipping é uma área retangular em um objeto gráfico. Qualquer operação gráfica deverá afetar
apenas os pontos contidos na área de corte. Pontos fora da área de corte não serão afetados por
qualquer operação gráfica.
public void setClip( definir a área de corte para o retângulo
int x, int y, int width, int height) especificado pelas coordenadas
public int getClipX() retornar o valor X da área de corte atual,
relativo à origem deste contexto gráfico
public int getClipY() retornar o Y da área de corte atual, relativo
à origem deste contexto gráfico
public int getClipWidth() retornar a largura da área de corte atual
public int getClipHeight() retornar a altura da área de corte atual

3.5. Pontos Âncora

Textos são desenhados por um ponto inicial. O método drawString() espera as coordenadas
localizada nos parâmetros x e y que são relativos aos pontos iniciais.

Desenvolvimento de Aplicações Móveis 11


JEDITM

public void drawString(String str, int x, int y, int anchor)

O ponto deve ser uma combinação da constante horizontal (LEFT, HCENTER ou RIGHT) e a
constante vertical (TOP, BASELINE ou BOTTOM). As constantes horizontal e vertical devem ser
combinadas utilizando o operador OU (|), por exemplo para desenhar o texto pela base horizontal
do centro, será necessário um valor inicial de BASELINE | HCENTER.

TOP | HCENTER

TOP | LEFT TOP | RIGHT

BASELINE | LEFT BASELINE | RIGHT

BOTTOM | LEFT BOTTOM | RIGHT

BOTTOM | HCENTER

BASELINE | HCENTER

Figura 4: Texto dos pontos iniciais

3.6. Desenhando Textos

Os métodos para desenhar texto e caracteres são:


public void drawString( desenhar o texto definido pelo parâmetro str
String str, utilizando a cor e fonte atual. Os parâmetros x e y
int x, int y, são as coordenadas do ponto inicial
int anchor)
public void drawSubstring( desenhar de maneira semelhante ao método
String str, drawString, exceto pelos parâmetros offset que limita
int offset, a base-zero e len que é responsável pelo tamanho
int len,
int x, int y,
int anchor)
public void drawChar( desenhar o caractere usando a cor da fonte atual
char character,
int x,
int y,
int anchor)
public void drawChars( desenhar os caracteres contidos no parâmetro data,
char[] data, iniciando pelo índice definido no parâmetro offset
int offset, com o tamanho especificado em length
int length,
int x, int y,
int anchor)

Aqui estão alguns métodos da classe Font que são úteis para o desenho de texto:
public int getHeight() retornar a altura do texto desta fonte. O tamanho
retornado inclui os espaçamentos extras. Isto assegura
que o desenho de dois textos com esta distância a
partir de um ponto de âncora para outro ponto de

Desenvolvimento de Aplicações Móveis 12


JEDITM

âncora conterá espaço suficiente entre as duas linhas


de texto
public int stringWidth(String str) retornar a largura total em pixels do espaço ocupado
pelo parâmetro str se for escrito utilizando-se esta
fonte
public int charWidth(char ch) retornar a largura total em pixels do espaço ocupado
pelo parâmetro ch se for escrito utilizando-se esta fonte
public int getBaselinePosition() retornar a distância em pixels entre o TOPO e a BASE
do texto, baseado nesta fonte

g.setColor(255, 0, 0); // vermelho


g.drawString("JEDI",
getWidth()/2, getHeight()/2,
Graphics.TOP | Graphics.HCENTER);
g.setColor(0, 0, 255); // azul
Font font = g.getFont();
g.drawString("Java Education & Development Initiative",
getWidth()/2, getHeight()/2+font.getHeight(),
Graphics.TOP | Graphics.HCENTER);

Figura 5: Saída de operações de drawString()

3.7. Desenhando Linhas

O único método da classe Graphics utilizado para desenhar linhas é definido como:
public void drawLine(int x1, int y1, int x2, int y2)

Este método desenha uma linha utilizando a cor e estilo corrente entre a coordenada inicial
indicada nos parâmetros x1 e y1 e a coordenada final indicada nos parâmetros x2 e y2.
g.setColor(255, 0, 0); // vermelho
// linha do canto superior esquerdo para o canto inferior direito da tela
g.drawLine(0, 0, getWidth()-1, getHeight()-1);

g.setColor(0, 255, 0); // verde


// linha horizontal no meio da tela
g.drawLine(0, getHeight()/2, getWidth()-1, getHeight()/2);

g.setColor(0, 0, 255); // azul


// linha horizontal na parte inferior da tela
g.drawLine(0, getHeight()-1, getWidth()-1, getHeight()-1);

g.setColor(0, 0, 0); // preta

Desenvolvimento de Aplicações Móveis 13


JEDITM

// linha do canto inferior esquerdo para o canto superior direito da tela


g.drawLine(0, getHeight()-1, getWidth()-1, 0);

Figura 6: Saída de chamadas a métodos de drawLine()

3.8. Desenhando Retângulos

Os métodos da classe Graphics para desenhar retângulos são:


public void drawRect(int x, int y, int width, int height)
public void drawRoundRect(int x, int y,
int width, int height,
int arcWidth, int arcHeight)
public void fillRect(int x, int y, int width, int height)
public void fillRoundRect(int x, int y,
int width, int height,
int arcWidth, int arcHeight)

O método drawRect desenha um retângulo com o canto superior esquerdo nas coordenadas
definidas pelos parâmetros x e y e com área definida em width+1 e height+1. Os mesmos
parâmetros estão no método drawRoundRect. Os parâmetros adicionais arcWidth e arcHeight são
os diâmetros horizontal e vertical do arco dos quatro cantos.
Note que a definição dos métodos drawRect e drawRoundRect especificam que o tamanho do
retângulo desenhado na tela é width+1 e a altura é height+1. Isto não é muito intuitivo,
entretanto é desta forma que estes métodos foram definidos para as especificações de MIDP.
Para agravar esta inconsistência de adicionar 1 ao tamanho real, os métodos fillRect e
fillRoundRect preenchem um retângulo de área especificada por width e height. Devido a esta
inconsistência pode-se colocar os mesmos parâmetros para os métodos drawRect, fillRect,
drawRoundRect e fillRoundRect. As bordas da direita e da parte inferior do retângulo desenhado
pelo método drawRect estarão além da área preenchida por fillRect.
// use tinta preta para drawRect
g.setColor(0, 0, 0);
g.drawRect(8, 8, 64, 32);

// use tinta amarela para fillRect


// para mostrar a diferença entre drawRect e fillRect
g.setColor(255, 255, 0);
g.fillRect(8, 8, 64, 32);

Desenvolvimento de Aplicações Móveis 14


JEDITM

Figura 7: Saída usando os mesmos parâmetros para drawRect e fillRect

// Define a cor da caneta para preto


g.setColor(0, 0, 0);

// Desenha um retângulo na posição inicial 4 e 8 com largura 88 e altura 44


// o retângulo superior esquerdo
g.drawRect(4,8,88,44);

// Desenha um retângulo arredondado no canto superior direito


g.drawRoundRect(108,8,88,44,18,18);

// Desenha um retângulo no canto inferior esquerdo


g.fillRect(4,58,88,44);

// Desenha um retângulo no canto inferior direito


g.fillRoundRect(108,58,88,44,18,18);

Figura 8: drawRect(), fillRect(), drawRoundRect() e fillRoundRect()

3.9. Desenhando Arcos

Os métodos para desenhar arcos circulares ou elípticos são:

Desenvolvimento de Aplicações Móveis 15


JEDITM

public void drawArc( desenhar um arco com centro em (x,y) e


int x, int y, dimensões (largura+1 x altura+1). O arco
int largura, desenhado inicia-se em anguloInic e se estende
int altura,
int anguloInic, por anguloArco graus. 0 (zero) grau está na
int anguloArco) posição 3 horas
public void fillArc( desenhar um um arco circular ou elíptico com
int x, int y, toda a sua área coberta com a cor atual
int largura,
int altura,
int anguloInic,
int anguloArco)

g.setColor(255, 0, 0);
g.drawArc(18, 18, 50, 50, 0, 360); // desenha um círculo
g.setColor(0, 255, 0);
g.drawArc(40, 40, 100, 120, 0, 180);
g.setColor(0, 0, 255);
g.fillArc(100, 200, 80, 100, 0, 90);

Figura 9: Saída da chamada aos métodos drawArc e fillArc

3.10. Desenhando imagens

Imagens são desenhadas usando o método drawImage() que possui a seguinte assinatura:
public void drawImage(Image img, int x, int y, int anchor)

Atenção: No NetBeans as imagens devem ser colocadas na pasta src do projeto.


Do mesmo modo que no método drawString(), os parâmetros x e y são as coordenadas do ponto
de âncora. A diferença é que a constante vertical da âncora é VCENTER em vez de BASELINE.
A âncora deve ser uma combinação de uma constante horizontal (LEFT, HCENTER ou RIGHT) e de
uma vertical (TOP, VCENTER ou BOTTOM). As constantes horizontal e vertical devem ser
combinadas usando o operador de bit OR (|). Isto significa que desenhar texto relativo ao centro
vertical e ao centro horizontal da imagem requer uma âncora de valor VCENTER | HCENTER.

Desenvolvimento de Aplicações Móveis 16


JEDITM

TOP | HCENTER

TOP | LEFT TOP | RIGHT

VCENTER | LEFT VCENTER | RIGHT

BOTTOM | LEFT BOTTOM | RIGHT

VCENTER | HCENTER BOTTOM | HCENTER

Figura 10: Pontos de âncora da imagem

try {
Image image = Image.createImage("/jedi.png");
g.drawImage(image,
getWidth()/2, getHeight()/2,
Graphics.VCENTER | Graphics.HCENTER);
} catch (Exception e){}

Figura 11: Saída do método drawImage()

3.11. Programa Final

Iremos combinar as idéias aprendidas nesta lição para obtermos o logo do JEDI na tela do celular.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class LogoMidlet extends MIDlet {


private Display display;
private LogoJedi canvas;

Desenvolvimento de Aplicações Móveis 17


JEDITM

public void startApp() {


if (display == null){
canvas = new LogoJedi(this);
display = Display.getDisplay(this);
}
display.setCurrent(canvas);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
protected void Quit(){
destroyApp(true);
notifyDestroyed();
}
}
class LogoJedi extends Canvas implements CommandListener {
private Command exitCommand = new Command("Exit", Command.EXIT, 0);
private LogoMidlet midlet;
private String text;
public LogoJedi(LogoMidlet midlet) {
this.midlet = midlet;
addCommand(exitCommand);
setCommandListener(this);
}
protected void paint(Graphics g) {
// Limpa a janela
g.setColor(255, 255, 255 );
g.fillRect(0, 0, getWidth(), getHeight());
// Imagem
try {
Image image = Image.createImage("/jedi.png");
g.drawImage(image,
getWidth()/2, 80,
Graphics.VCENTER | Graphics.HCENTER);
} catch (Exception e){ }
// Texto
g.setColor(255, 0, 0); // vermelho
Font f =
Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE);
g.setFont(f);
g.drawString("JEDI", getWidth()/2, getHeight()/2,
Graphics.TOP | Graphics.HCENTER);
int tamFont = f.getHeight();

g.setColor(0, 0, 255); // azul


g.drawLine(20, getHeight()/2+tamFont, getWidth()-20, getHeight()/2+tamFont);
f = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL);
g.setFont(f);
g.drawString("Java Education & Development Initiative",
getWidth()/2, getHeight()/2+tamFont+3,
Graphics.TOP | Graphics.HCENTER);
}
public void commandAction(Command c, Displayable d) {
if (c == exitCommand){
midlet.Quit();
}
}
}

Desenvolvimento de Aplicações Móveis 18


JEDITM

4. Exercícios
4.1. Códigos de Teclas

Criar um MIDlet que mostre o código e o nome da tecla que foi pressionada pelo usuário. Utilizar
um Canvas para mostrar este código e o nome no centro da tela.

Desenvolvimento de Aplicações Móveis 19


Módulo 5
Desenvolvimento de Aplicações Móveis

Lição 5
Sistema de Gerenciamento de Registro

Versão 1.0 - Set/2007


JEDITM

1. Objetivos
MIDP fornece uma biblioteca de componentes onde os programas podem salvar os dados da
aplicação no dispositivo de modo físico. O Sistema de Gerenciamento de Registro do MIDP é o
meio pelo qual as MIDlets podem armazenar dados através de chamadas MIDlet. Os dados são
armazenados na memória não-volátil do dispositivo, ou seja, não serão perdidos mesmo que o
programa seja finalizado ou o aparelho seja desligado.
O Sistema de Gerenciamento de Registro é um sistema de banco de dados orientados a registro
para dispositivos MIDP. Um registro neste banco de dados é simplesmente um array de bytes com
um índice.

Ao final desta lição, o estudante será capaz de:


• Entender o conceito de RecordStore
• Criar ou abrir um RecordStore
• Adicionar, recuperar, atualizar e excluir registros
• Fechar um RecordStore
• Enumerar registros utilizando um RecordEnumerator
• Criar um RecordComparator
• Criar um RecordFilter

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. RecordStore
Record Store é uma coleção de registros, a classe é encontrada no pacote javax.microedition.rms.
Identificadores de registro (Record ID) em um banco de dados são únicos. Esses identificadores
são automaticamente alocados no momento da criação do registro e funcionam como um índice
ou chave primária. São atribuídos sequencialmente, sendo que o primeiro ID de cada banco de
dados possui o valor igual a 1 (um).
Quando um registro é excluído, seu identificador não será reutilizado. Se criarmos quatro
registros e excluirmos o quarto, o próximo identificador que o sistema atribuirá será igual a 5 (ver
Figura).

Record ID Array de bytes


1 Dado do registro #1
2 Dado do registro #2
3 Dado do registro #3
5 Dado do registro #5

Aplicativos MIDlets podem criar mais de um objeto RecordStore. O nome do RecordStore deve ser
único dentro de uma suite MIDlet. São sensíveis ao contexto (maiúsculas diferentes de
minúsculas) e devem possuir um tamanho máximo de 32 caracteres.
Quando uma aplicação MIDlet é eliminada de um dispositivo, todos os RecordStore associados a
esta aplicação também serão eliminados.

2.1. Criando ou Abrindo um RecordStore

Os métodos listados abaixo criam ou abrem para uso um RecordStore:


static RecordStore openRecordStore(
String recordStoreName,
boolean createIfNecessary)
static RecordStore openRecordStore(
String recordStoreName,
boolean createIfNecessary,
int authmode,
boolean writable)
static RecordStore openRecordStore(
String recordStoreName,
String vendorName,
String suiteName)

Caso o parâmetro createIfNecessary possuir o valor true e o RecordStore não existir, este será
automaticamente criado. Se o parâmetro possuir o valor false e o RecordStore não existir, uma
exceção do tipo RecordStoreNotFoundException será lançada.
O parâmetro authmod poderá assumir um dos seguintes atributos constantes existentes:
RecordStore.AUTHMODE_PRIVATE ou RecordStore.AUTHMODE_ANY. Utilizando o primeiro atributo
o RecordStore será acessível apenas pela aplicação MIDlet que o criou. Utilizando o segundo
atributo o RecordStore será acessível por qualquer aplicação MIDlet. O modo de acesso é
especificado pelo parâmetro lógico writable. Para permitir que outros MIDlets (fora do aplicativo
que o criou) armazenem ou leiam dados neste Record Store, este parâmetro deverá ser informado
com o valor true.
Utilizando a primeira forma do método openReacordStore() o resultado obtido é que o
RecordStore será acessível somente aos MIDlets da mesma aplicação (o valor do atributo
authmode terá o mesmo valor da constante AUTHMODE_PRIVATE).
Para abrir um RecordStore existente a partir de uma aplicação MIDlet diferente, a terceira forma
do método openRecordStore é utilizada. Os parâmetros vendorName e suiteName deverão ser

Desenvolvimento de Aplicações Móveis 5


JEDITM

especificados contendo o nome do desenvolvedor e o nome do aplicativo.


Se o RecordStore já estiver aberto, estes métodos irão retornar sua referência deste. O sistema
mantém armazenado quantas vezes foram abertos os RecordStore. É obrigatório que RecordStore
seja fechado o mesmo número de vezes que tenha sido aberto.

2.2. Adicionando Registros

O seguinte método é responsável pelas adições de novos registros em um RecordStore:


int addRecord(byte[] data, int offset, int numBytes)

O método addRecord criará um novo registro no banco de dados e retornará o seu identificador
(Record ID). As informações recebidas como parâmetro neste método são:
1. Um array de bytes contendo os dados a serem armazenados
2. Um atributo inteiro contendo a posição inicial do array enviado
3. A quantidade total de elementos do array

2.3. Recuperando Registros

Os seguintes métodos são responsáveis pelas buscas de dados em um Record Store:


byte[] getRecord(
int recordId)
int getRecord(
int recordId, byte[] buffer, int offset)
int getRecordSize(
int recordId)

A primeira forma do método getRecord devolve uma cópia dos dados armazenados no registro
com a Record ID especificada pelo parâmetro. A segunda forma copiará os dados no parâmetro
(array de bytes) fornecido Ao utilizar esta forma, o array de bytes precisa estar previamente
alocado. Caso o tamanho do registro seja maior que o tamanho do parâmetro uma exceção
ArrayIndexOutOfBoundsException será lançada. O método getRecordSize é utilizado de modo a
obter o tamanho do registro que será lido.

2.4. Modificando Registros

Não se pode modificar somente uma parte do registro. Para modificar um registro é necessário:
1. Ler o registro utilizando o método getRecord
2. Modificar o registro na memória
3. Chamar o método setRecord para modificar os dados do registro

O método setRecord possui a seguinte assinatura:

void setRecord(
int recordId, byte[] newData, int offset, int numBytes)

2.5. Eliminando Registros

O seguinte método é responsável pelas eliminações dos dados em um RecordStore:


void deleteRecord(
int recordId)

Quando um registro é eliminado, o ID deste registro não será reutilizado para as próximas
inclusões. Isto significa que podem ocorrer buracos entre os registros. Deste modo não é
aconselhável utilizar um incrementador para listar todos os registros de um Record Store. Um

Desenvolvimento de Aplicações Móveis 6


JEDITM

RecordEnumerator deve ser utilizado para percorrer a lista de registros guardados.

2.6. Fechando um RecordStore

O seguinte método é responsável pelo fechamento de um RecordStore:


void closeRecordStore()

Ao chamar o método closeRecordStore(), o RecordStore não será fechado até que seu valor seja
idêntico ao número de chamadas do método openRecordStore(). O sistema armazena o número
de vezes que um Record Store foi aberto. Caso o número de chamadas ao método
closeRecordStore() sejam superiores ao número de chamadas ao método openRecordStore(), será
disparada uma exceção do tipo RecordStoreNotOpenException.
Este trecho de código mostra um exemplo de um MIDlet simples que exemplifica a criar, adição e
localização de dados em um RecordStore:
// Abre ou Cria um RecordStore com o nome "RmsExample1"
recStore = RecordStore.openRecordStore("RmsExample1", true);

// Lê o conteúdo do RecordStore
for (int recId=1; recId <= recStore.getNumRecords(); recId++){
// O método getRecord returna o tamanho do registro
recLength = recStore.getRecord(recId, data, 0);

// Converte para String um array de bytes


String item = new String(data, 0, recLength);
...
}

// Este será o dado a ser guardado


String newItem = "Record #" + recStore.getNextRecordID();

// Converte a String para o array de bytes


byte[] bytes = newItem.getBytes();

// Grava o dado no RecordStore


recStore.addRecord(bytes, 0, bytes.length);

Dicas de Programação:

1. O Registro começa com o valor de ID igual a 1, e não 0. Deve-se tomar cuidado ao se utilizar
laços.
2. É melhor utilizar um RecordEnumerator do que um índice incrementado (como neste
exemplo). Registros eliminados ainda serão lidos por este exemplo e causará uma exceção do
tipo InvalidRecordIDException.

2.7. Obtendo uma Lista de RecordStore de uma aplicação MIDlet

O seguinte método é responsável pela obtenção de uma lista de RecordStore existente:


static String [] listRecordStores()

O método retorna um conjunto de nomes de RecordStore que foram criados pela aplicação
MIDlet. Se a aplicação não possuir nenhum RecordStore é retornado o valor null.
String[] storeNames = RecordStore.listRecordStores();
System.out.println("RecordStore for this MIDlet suite:");

for (int i=0; storeNames != null && i < storeNames.length; i++) {


System.out.println(storeNames[i]);
}

Este é um exemplo de saída:

Desenvolvimento de Aplicações Móveis 7


JEDITM

Record Stores for this MIDlet suite:


Prefs
RmsExample1
RmsExample2

A ordem dos nomes retornados não está definida e é dependente da implementação. Então, ao se
desejar exibir os nomes ordenados alfabeticamente, primeiro deve-se realizar uma ordenação
deste array.

2.8. Guardando Tipos Primitivos de Java

Até o momento, os dados que temos gravados e lidos no RecordStore são Strings. CLDC inclui as
classes padrões para manipulação de tipos primitivos. Estas classes são da biblioteca padrão da
Plataforma Java Standard Edition (Java SE).
Pode-se gravar tipos primitivos combinando as classes ByteArrayOutputStream e
DataOutputStream. Para ler tipos primitivos (int, long, short, char, boolean, etc) pode-se também
utilizar as classes ByteArrayInputStream e DataInputStream.
ByteArrayOutputStream out = new ByteArrayOutputStream();
DataOutputStream dOut = new DataOutputStream(out);

// Armazenando um inteiro
dOut.writeInt(recStore.getNextRecordID() * recStore.getNextRecordID());

// Armazenando uma String


dOut.writeUTF("Record #" + recStore.getNextRecordID());

// Transformando para um array de bytes


byte[] bytes = out.toByteArray();

// Armazenando os dados no Record Store


recStore.addRecord(bytes, 0, bytes.length);
...

// Obtendo o próximo registro


byte[] recBytes = enumerator.nextRecord();

// Para resgatar os valores


ByteArrayInputStream in = new ByteArrayInputStream(recBytes);
DataInputStream dIn = new DataInputStream(in);
int count = dIn.readInt();
String item = dIn.readUTF();

2.9. Outros métodos para um RecordStore

Os seguintes métodos podem ser utilizados para obter dados de um RecordStore:


long getLastModified()
int getVersion()

O sistema armazena a informação de quando um Record Store foi modificado pela última vez.
Este valor pode ser obtido através do método getLastModified() e retorna um valor do tipo long,
que poderá ser utilizado pelo método System.currentTimeMillis().
O RecordStore mantém a informação da versão que pode ser obtida através do método
getVersion(). Toda vez em que um registro é modificado, seu número de versão é atualizado.
Utilizando-se qualquer um dos métodos addRecord, setRecord e deleteRecord o número de versão
do objeto será incrementado.
Outros métodos que podem ser utilizados são:
static void deleteRecordStore( Eliminar a RecordStore que contenha o nome fornecido
String nomeRecordStore)
String getName() Retornar o nome do RecordStore

Desenvolvimento de Aplicações Móveis 8


JEDITM

int getNextRecordID() Retornar o recordId do próximo registro a ser adicionado ao objeto


RecordStore
int getNumRecords() Retornar o número de registros atualmente no RecordStore
int getSize() Retornar a quantidade de bytes ocupados em um RecordStore
int getSizeAvailable() Retornar a quantidade de bytes disponíveis em um RecordStore
void setMode( Modificar o modo de acesso de um RecordStore
int authmode,
boolean writable)

Desenvolvimento de Aplicações Móveis 9


JEDITM

3. Enumeração de Registro
Percorrer registros incrementando um índice é uma forma ineficiente de agir. O RecordStore pode
ter seus registros eliminados que geram buracos no RecordId.
A utilização de um RecordEnumeration resolve este problema sem ter de tratar os registros
excluídos. É possível também ordenar um objeto do tipo RecordEnumeration utilizando-se o
método comparator(). Utilizando-se o método filter(), pode-se saltar os registros que não são
importantes para o resultado.
RecordEnumeration enumerateRecords(
RecordFilter filter,
RecordComparator comparator,
boolean keepUpdated)

O método enumerateRecords de um RecordStore retornará um objeto do tipo RecordEnumeration,


no qual é permitido obter todos os registros de um RecordStore. Essa é a maneira recomendada
para percorrer todos os registros armazenados. Iremos discutir os objetos filter e comparator nas
próximas seções.
O modo mais simples para utilizar esse método é informar o valor null para os parâmetros filter e
comparator. Obteremos como retorno da execução deste método um objeto do tipo
RecordEnumeration de todos os registros do Record Store ordenados de uma maneira indefinida.
// Abrir ou criar um Record Store com o nome "RmsExample2"
recStore = RecordStore.openRecordStore("RmsExample2", true);

// Carregar o conteúdo do Record Store


RecordEnumeration enumerator = recStore.enumerateRecords(null, null, false);
while (enumerator.hasNextElement()){
// Obter o próximo registro e converter um array de byte em String
String item = new String(enumerator.nextRecord());

// Realiza qualquer instrução com o dado


...
}

Desenvolvimento de Aplicações Móveis 10


JEDITM

4. Comparador de Registros
A ordenação de um objeto do tipo RecordEnumeration pode ser definida utilizando-se um
RecordComparator. Um RecordComparator é passado para o método enumerateRecords. Caso
seja necessário ordenar um objeto do tipo RecordEnumeration, isso pode ser feito definindo-se
um objeto do tipo RecordComparator e enviando-o como o segundo parâmetro para o método
enumerateRecords.
int compare(byte[] reg1, byte[] reg2)

Para criar um RecordComparator, é preciso primeiro implementar a interface RecordComparator.


Essa interface define um único método compare(byte[] reg1, byte[] reg2). Esse método precisa
retornar RecordComparator.FOLLOWS ou RecordComparator.PRECEDES se reg1 procede ou
antecede reg2 na ordem, respectivamente. RecordComparator.EQUIVALENT pode ser retornado se
reg1 for igual a reg2 na ordem.
// Criar uma enumeration, ordenada alfabeticamente
RecordEnumeration enumerator = recStore.enumerateRecords(
null, new AlphaOrder(), false);

// Classe que ordena alfabeticamente


class AlphaOrder implements RecordComparator {
public int compare(byte[] reg1, byte[] reg2){
String registro1 = new String(reg1).toUpperCase();
String registro2 = new String(reg2).toUpperCase();
if (registro1.compareTo(registro2) < 0){
return(RecordComparator.PRECEDES);
} else {
if (registro1.compareTo(registro2) > 0){
return(RecordComparator.FOLLOWS);
} else {
return(RecordComparator.EQUIVALENT);
}
}
}
}

Desenvolvimento de Aplicações Móveis 11


JEDITM

5. Filtro de Registros
Os exemplos que vimos até o momento somente percorrem e lêem todos registros de um Record
Store. Entretanto, podemos utilizar um filter para obtermos somente os registros que são
necessários.
A classe necessita implementar o método match() para selecionar somente os registros válidos
em um determinado contexto.
boolean matches(byte[] candidate)

O retorno é um atributo do tipo lógico que deve conter o valor true caso o registro seja compatível
com o critério definido. Caso contrário é retornado false.
public boolean matches(byte[] candidate){
boolean resultado = false;
try {
ByteArrayInputStream bin = new ByteArrayInputStream(candidate);
DataInputStream dIn = new DataInputStream(bin);
int count = dIn.readInt();
String item = dIn.readUTF();

// Retornar somente registros com o conteúdo terminado com 0


resultado = item.endsWith("0");
} catch (Exception e) {
}
return(resultado);
}

Desenvolvimento de Aplicações Móveis 12


JEDITM

6. Consumo de Combustível
Iremos ajuntar algumas idéias aprendidas até o momento e montar um aplicativo prático para
controlar o consumo de combustível do nosso carro.
Ao pensarmos em aplicativos para aparelhos móveis devemos ter em mente as seguintes regras:
1. Praticidade
2. Facilidade
O usuário estará no posto abastecendo o carro. Ao acessar o aplicativo precisamos obter duas
informações: a quilometragem atual do veículo e a quantidade de combustível inserida. Neste
primeiro registro ainda não teremos dados suficientes para o cálculo do consumo, entretanto, a
partir do segundo registro já poderemos calcular o consumo, obtido pela fórmula:
(quilometragem atual – quilometragem anterior) / quantidade combustível

Uma outra idéia interessante é guardar somente as últimas 11 entradas, deste modo
mostraremos as 10 últimas médias de consumo. Antes de verificar as classes descritas abaixo,
tente resolver este aplicativo.
A primeira classe chamada PrincipalMidlet. Conforme vimos anteriormente, o método startApp()
fará a chamada a um objeto do tipo Canvas.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class PrincipalMidlet extends MIDlet {


private MenuCanvas canvas;
public Display display;

public void startApp() {


if (display == null) {
canvas = new MenuCanvas(this);
display = Display.getDisplay(this);
}
display.setCurrent(canvas);
}
protected void pauseApp() {
}
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
}
}

A segunda classe, chamada MenuCanvas, do tipo Canvas, conterá toda a regra de negócio.
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.*;

public class MenuCanvas extends Canvas implements CommandListener {

private PrincipalMidlet pai;


private InsertForm form;
private byte opc = 0;
private Command exitCmd = new Command("Exit", Command.EXIT, 0);
private Command insertCmd = new Command("Insert", Command.OK, 1);
private Command reportCmd = new Command("Report", Command.OK, 1);
// Comandos para o Form
private Command backCmd = new Command("Back", Command.BACK, 2);
private Command saveCmd = new Command("Save", Command.OK, 1);
// Atributos para o Registro
private String regGas;
private RecordStore rsGas;

public MenuCanvas(PrincipalMidlet pai) {

Desenvolvimento de Aplicações Móveis 13


JEDITM

// Cria ou abre o registro


try {
rsGas = RecordStore.openRecordStore("Gasolina", true);
} catch (RecordStoreException ex) {
ex.printStackTrace();
}
this.pai = pai;
setCommandListener(this);
addCommand(exitCmd);
addCommand(insertCmd);
addCommand(reportCmd);
}
public void paint(Graphics g) {
g.setColor(255, 255, 255);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0, 64, 128);
switch (opc) {
case 0:
g.drawString("Principal Menu", 0, 0,
Graphics.TOP | Graphics.LEFT);
g.setColor(0, 114, 168);
g.drawString("Use this program for the control", 0, 24,
Graphics.TOP | Graphics.LEFT);
g.drawString("the consumption of gasoline in your car", 0, 36,
Graphics.TOP | Graphics.LEFT);
g.drawString("Options:", 0, 64, Graphics.TOP | Graphics.LEFT);
g.drawString("INSERT - Use for add record Km/Lt", 0, 76,
Graphics.TOP | Graphics.LEFT);
g.drawString("REPORT - Use for list the 10 last consumer", 0, 88,
Graphics.TOP | Graphics.LEFT);
g.drawString("Fernando Anselmo", 0, 112, Graphics.TOP | Graphics.LEFT);
break;
case 1:
int lin = 0;
g.drawString("Report Option", 0, lin, Graphics.TOP | Graphics.LEFT);
lin += 14;
g.drawString("Your 10 last consumes is...", 0, lin,
Graphics.TOP | Graphics.LEFT);
lin += 12;
int od1 = 0;
int od2 = 0;
double gas = 0.0;
g.setColor(0, 114, 168);
try {
if (rsGas.getNumRecords() > 1) {
for (int recId = 1; recId < rsGas.getNumRecords(); recId++) {
regGas = new String(rsGas.getRecord(recId));
od1 = Integer.parseInt(regGas.substring(0, regGas.indexOf("|")));
gas = Double.parseDouble(regGas.substring(
regGas.indexOf("|") + 1));
regGas = new String(rsGas.getRecord(recId + 1));
od2 = Integer.parseInt(regGas.substring(0, regGas.indexOf("|")));
lin += 15;
g.drawString(recId + ") " + ((od2 - od1) / gas) +
" Km/Lt", 0, lin, Graphics.TOP | Graphics.LEFT);
}
}
} catch (RecordStoreException ex) {
ex.printStackTrace();
}
break;
}
}
public void commandAction(Command command, Displayable displayable) {
if (command == exitCmd) {
try {
rsGas.closeRecordStore();

Desenvolvimento de Aplicações Móveis 14


JEDITM

pai.destroyApp(true);
pai.notifyDestroyed();
} catch (RecordStoreException ex) {
ex.printStackTrace();
} catch (MIDletStateChangeException ex) {
ex.printStackTrace();
}
} else if (command == insertCmd) {
repaint();
form = new InsertForm("Insert Option");
pai.display.setCurrent(form);
} else if (command == reportCmd) {
opc = 1;
repaint();
} else if (command == backCmd) {
pai.display.setCurrent(this);
opc = 0;
repaint();
} else if (command == saveCmd) {
regGas = form.getRegGas();
byte[] data;
try {
if (rsGas.getNumRecords() == 11) {
for (int recId = 1; recId < rsGas.getNumRecords(); recId++) {
data = rsGas.getRecord(recId + 1);
rsGas.setRecord(recId, data, 0, data.length);
}
data = regGas.getBytes();
rsGas.setRecord(11, data, 0, data.length);
} else {
data = regGas.getBytes();
rsGas.addRecord(data, 0, data.length);
}
} catch (RecordStoreException ex) {
ex.printStackTrace();
}
pai.display.setCurrent(this);
opc = 0;
repaint();
}
}
class InsertForm extends Form {
private TextField QTDGAS;
private TextField ODOMETER;

public InsertForm(String title) {


super(title);
addCommand(backCmd);
addCommand(saveCmd);
setCommandListener(MenuCanvas.this);
ODOMETER = new TextField("Odometer (Kilometer):", "", 64,
TextField.NUMERIC);
QTDGAS = new TextField("Gasoline (Liter):", "", 64, TextField.DECIMAL);
this.append(new StringItem("Inform your consumption", ""));
this.append(ODOMETER);
this.append(QTDGAS);
}
public String getRegGas() {
return ODOMETER.getString() + "|" + QTDGAS.getString();
}
}
}

Começaremos pelo construtor que montará a janela principal. Observe que o atributo opc do tipo
byte possuirá o controle sobre qual visão da aplicação temos. O construtor também inicializa o
objeto do tipo RecordStore (chamado rsGas). Logo em seguida o método paint será chamado e

Desenvolvimento de Aplicações Móveis 15


JEDITM

mostrará a seguinte janela:

Figura 1: Janela do Menu Principal

Caso o comando seja “Insert”, passaremos para o método commandAction. Neste momento
montaremos um objeto da classe Form e a definiremos como principal. Montaremos com este
formulário a seguinte janela:

Figura 2: Janela da Opção de Inclusão

O usuário entrará com a informação da quantidade de quilômetros percorridos pelo carro até o
momento e a quantidade de litros abastecidos. Pode-se escolher a opção “Save” para salvar o
registro, ou “Back” para retornar ao menu principal.
Caso seja selecionada a opção “Save”, realizaremos a verificação se já foram inseridos 11
registros. Em caso afirmativo, movimentaremos o registro que ocupa a segunda posição para a
primeira, o que ocupa a terceira posição para a segunda e deste modo sucessivamente até que se
libere a décima primeira posição, onde guardaremos este novo registro. Em caso negativo,
simplesmente inserimos mais um registro ao RecordStore.
No menu principal, caso o comando seja “Report”, passaremos para o método commandAction,

Desenvolvimento de Aplicações Móveis 16


JEDITM

mudando o atributo opc e montamos agora a seguinte janela:

Figura 3: Janela da Opção de Relatório

Agora fica por conta do método paint localizar os registros informados e realizar o cálculo
conforme vimos, lembrando que o método indexOf(String) localiza uma determinada posição
dentro de um objeto String e o método substring(int1, int2) localiza uma determinada posição e
retorna todos os caracteres a partir desta enquanto a posição seja menor que o segundo valor.
Note que este projeto está bem compacto e ainda permite diversas melhorias, tais como:
● Imagens
● Opções para o usuário selecionar quantos registro deseja armazenar
● Um gráfico para mostrar o consumo ao invés de informação textual
Então, utilize-o como aplicativo base para praticar.

Desenvolvimento de Aplicações Móveis 17


JEDITM

7. Exercícios
7.1. Armazenar Preferências

Criar um aplicativo MIDlet que possa armazenar as preferências de um programa. Este aplicativo
irá armazenar as preferências em um objeto do tipo RecordStore. Cada registro irá conter o nome
de uma variável e seu valor. Cada conjunto variável e valor será armazenado em um único
registro.
Dica: Pode-se implementar os métodos:
public String ler(RecordStore recStore, String nome, String valorPadrao);
public void escrever(RecordStore recStore, String nome, String valor);

Desenvolvimento de Aplicações Móveis 18


Módulo 5
Desenvolvimento de Aplicações Móveis

Lição 6
Redes

Versão 1.0 - Set/2007


JEDITM

1. Objetivos
Nesta lição, iremos estudar como acessar redes utilizando MIDlets.

Ao final desta lição, o estudante será capaz de:


• Descrever o Framework Genérico de Conexão e como pode ser usado para suportar
diferentes métodos de conexão
• Especificar argumentos de conexão usando o formato de endereço URL do GCF
• Criar conexões HTTP/HTTPS
• Criar MIDlets usando soquetes TCP e datagramas UDP

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. Framework Genérico de Conexão


O Framework Genérico de Conexão suporta as conexões baseadas em pacotes (através de
Soquetes) e baseadas em stream (através de Datagramas). Como o nome diz, este framework
provê uma API básica para conexões em CLDC. Determina uma base comum para conexões como
HTTP, soquetes e datagramas. Mesmo bluetooth e serial I/O tem um lugar neste framework.
Fornece um conjunto genérico e comum da API que abstrai todos os tipos de conexões. Deve-se
notar que nem todos os tipos de conexão devem ser requeridos para ser implementados pelos
dispositivos MIDP.

2.1. Hierarquia da Interface GCF

A hierarquia extensível da interface do GCF torna possível a generalização. Um novo tipo de


conexão pode ser adicionado a este framework através da extensão desta hierarquia.

Figura 1: Hierarquia da Interface GCF

2.2. A URL de Conexão GCF

Argumentos de conexão são especificados usando o formato de endereçamento:


scheme://username:password@host:port/path;parameters

1. scheme (esquema) é o protocolo ou método de conexão. Exemplos de esquemas são: http, ftp
e https
2. username (nome do usuário) é opcional, entretanto, caso seja especificado, um @ deve
preceder o host
3. password (senha do usuário) é opcional e pode ser especificada somente se o username
estiver presente. Se a password estiver presente, deve estar separada do username por dois
pontos (:)

Desenvolvimento de Aplicações Móveis 5


JEDITM

4. host (domínio) este argumento é obrigatório. Pode ser um hostname, um nome completo no
domínio (FQDN) ou o endereço IP do host alvo.
5. port (porta) este argumento é opcional. Caso não seja especificado, a porta padrão para o
esquema será utilizada.
6. path (caminho)
7. parameters (argumentos) este é opcional, mas deve estar precedido por ponto e vírgula
quando presente
Se utilizarmos colchetes para definir os argumentos opcionais neste formato de endereçamento,
poderemos expressar da seguinte forma:
scheme://[username[:password]@]host[:port]/path[;parameters]

O Uniform Resource Indicator (URI) está definido no RFC 2396, que é a base para este formato de
endereçamento. No MIDP 2.0, somente os esquemas "http" e "https" devem ser implementados
por estes dispositivos.

Desenvolvimento de Aplicações Móveis 6


JEDITM

3. Conexão HTTP
3.1. O Protocolo HTTP

HTTP significa Protocolo de Transporte de Hiper Texto. Este é o protocolo utilizado para transferir
páginas da WEB de seus servidores (ex. www.sun.com) para os WEB Browsers. O cliente (WEB
Browser) requisita uma página, especificando o seu caminho com os modelos do tipo "GET" ou
"POST".
Para o modelo "GET", argumentos são especificados e embutidos na URL. Por exemplo, para
passar um atributo com o nome "id" e valor 100 a página "index.jsp", a URL é especificada da
seguinte forma: "http://hostname/index.jsp?id=100". Argumentos adicionais são separados pelo
simbolo &, por exemplo: "http://hostname/index.jsp?id=100&page=2".
Quando o modelo "POST" é utilizado, argumentos não fazem parte da URL, mas são enviados em
linhas diferentes após o comando POST.
Cliente / Navegador WEB Servidor HTTP
GET /index.jsp?id=100 HTTP/1.1 HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Date: Wed, 18 Jun 2005 14:09:31 GMT
Connection: close

<html>
<head>
<title>Test Page</title>
</head>
<body>
<h1 align="center">Test Page</h1>
</body>
</html>
Figura 2: Exemplo de Transação HTTP GET

Cliente / Navegador WEB Servidor HTTP


GET /non-existent.html HTTP/1.0 HTTP/1.1 404 /non-existent.html
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 983
Date: Mon, 11 Jul 2005 13:21:01 GMT
Connection: close

<html><head><title>Apache Tomcat/5.5.7 - Error


report</title><style>...
<body><h1>HTTP Status 404</h1>
...
The requested resource (non-existent.html) is
not available.
...
</body></html>
Figura 3: Exemplo de transação HTTP GET com um response erro

3.2. Criando uma conexão HTTP

Podemos abrir uma conexão HTTP usando o método Connector.open() e fazer um casting com
uma das seguintes interfaces: StreamConnection, ContentConnection ou HTTPConnection.
Entretanto, com as interfaces StreamConnection e ContentConnection podemos especificar e
derivar parâmetros específicos do HTTP resultante.
Quando se utiliza a StreamConnection, o tamanho da resposta não pode ser determinado
previamente. Com a interface ContentConnection ou HTTPConnection existe a possibilidade de se
determinar o tamanho da resposta. Porém, o tamanho não está sempre disponível, então o

Desenvolvimento de Aplicações Móveis 7


JEDITM

programa deverá que ser capaz de recorrer a outros meios para obter a resposta sem o
conhecimento prévio do tamanho.
HttpConnection connection = null;
InputStream iStream = null;
byte[] data = null;

try {
connection = (HttpConnection) Connector.open("http://www.sun.com/");
int code = connection.getResponseCode();
switch (code) {
case HttpConnection.HTTP_OK:
iStream = connection.openInputStream();
int length = (int) connection.getLength();
if (length > 0){
data = new byte[length];
int totalBytes = 0;
int bytesRead = 0;
while ((totalBytes < length) && (bytesRead > 0)) {
bytesRead = iStream.read(
data, totalBytes, length - totalBytes);
if (bytesRead > 0){
totalBytes += bytesRead;
}
}
} else {
// Tamanho não é conhecido, ler por caracter
...
}
break;
default:
break;
}
...

3.3. Controlando Redirecionamentos HTTP

Algumas vezes o servidor redireciona o navegador do cliente para outras páginas WEB através de
uma resposta que pode ser através de uma HTTP_MOVED_PERM (301), HTTP_MOVED_TEMP
(302), HTTP_SEE_OTHER (303) ou HTTP_TEMP_REDIRECT (307) ao invés da resposta comum
HTTP_OK. O programa terá de ser capaz de detectar isso utilizando o método getResponseCode(),
obter a nova URI do cabeçalho utilizando o método getHeaderField("Localização") e recuperar
esse documento na nova localização.
int code = connection.getResponseCode();
switch(code){
case HttpConnection.HTTP_MOVED_PERM:
case HttpConnection.HTTP_MOVED_TEMP:
case HttpConnection.HTTP_SEE_OTHER:
case HttpConnection.HTTP_TEMP_REDIRECT:
String newUrl = conn.getHeaderField("Location");
...

Desenvolvimento de Aplicações Móveis 8


JEDITM

4. Conexões HTTPS
HTTPS é um modelo HTTP sobre uma conexão segura de transporte. A abertura de uma conexão
do tipo HTTPS é realizada de forma idêntica a abrir uma conexão do tipo HTTP. A única diferença é
que a URL é passada para Connector.open() e o resultado moldado para uma variável da classe
HttpsConnection.
Um tipo adicional de exceção pode ser lançada por um método Connector.open() ao invés das
exceções comuns IllegalArgumentException, ConnectionNotFoundException, java.io.IOException e
SecurityException. Uma exceção do tipo CertificateException pode ser disparada por causa de
falhas no certificado.
import javax.microedition.io.*;

HttpsConnection connection = null;


InputStream iStream = null;
byte[] data = null;
try {
connection = (HttpsConnection) Connector.open("https://www.sun.com/");
int code = connection.getResponseCode();
...
} catch (CertificateException ce){
switch (ce.getReason()){
case CertificateException.EXPIRED:
...
}
}

Todos as constantes listadas a seguir foram retiradas da especificação MIDP 2.0 – JSR 118 e são
do tipo byte estáticas.
BAD_EXTENSIONS Indicar que um certificado possui extensões críticas desconhecidas
BROKEN_CHAIN Indicar que um certificado numa cadeia não foi gerado pela próxima
autoridade na cadeia
CERTIFICATE_CHAIN_TOO_LONG Indicar que o tamanho da cadeia de servidores certificados excedeu o
tamanho permitido pela política do emissor
EXPIRED Indicar que um certificado expirou
INAPPROPRIATE_KEY_USAGE Indicar que a chave pública do certificado foi usada de maneira
considerada inapropriada pelo emissor
MISSING_SIGNATURE Indicar que um objeto certificado não contém uma assinatura
NOT_YET_VALID Indicar que um certificado ainda não é válido
ROOT_CA_EXPIRED Indicar que a chave pública da autoridade certificadora raiz (root CA)
expirou
SITENAME_MISMATCH Indicar que um certificado não contém o nome correto do site
UNAUTHORIZED_INTERMEDIATE_CA Indicar que um certificado intermediário na cadeia não possui a
permissão para ser uma autoridade certificadora intermediária
UNRECOGNIZED_ISSUER Indicar que um certificado foi emitido por uma entidade desconhecida
UNSUPPORTED_PUBLIC_KEY_TYPE Indicar que o tipo da chave pública no certificado não é suportado pelo
dispositivo
UNSUPPORTED_SIGALG Indicar que um certificado foi assinado utilizando um algoritmo não
suportado
VERIFICATION_FAILED Indicar uma verificação falha de certificado
Figura 4: Razões para uma exceção CertificateException

Desenvolvimento de Aplicações Móveis 9


JEDITM

5. Sockets TCP
A maior parte das implementações de HTTP funcionam no topo da camada TCP. Ao enviar dados
usando a camada TCP, estes podem ser divididos em pedaços menores chamado pacotes. A
camada TCP garante que todos os pacotes enviados pelo transmissor serão recebidos pelo
receptor na mesma ordem que eles foram enviados. Se um pacote não for recebido pelo receptor,
ele será reenviado. Isso significa que quando uma mensagem é enviada, podemos ter certeza que
ela será entregue para o receptor no mesmo formato na qual foi enviada, sem omissões ou
inserções (exceto em circunstâncias extremas como o receptor sendo desconectado da rede).
A camada TCP é o responsável pela remontagem dos pacotes e retransmissão de forma
transparente. Por exemplo, o protocolo HTTP não se preocupa com a montagem e desmontagem
de pacotes porque isso deve ser tratados pela camada TCP.
Algumas vezes, o tamanho da mensagem é muito pequeno e torna-se muito ineficiente para ser
enviado através de um único pacote (a sobrecarga do pacote é muito grande se comparada aos
dados a serem enviado). Imagine muitos pacotes percorrendo a rede com apenas um byte de
dados e muitos bytes de sobrecarga (digamos 16 bytes). Isso poderia fazer a rede ser muito
ineficiente, muitos pacotes inundariam a rede com alguns poucos bytes de dados.
Nesses casos, a implementação do TCP deve aguardar mensagens subsequentes para serem
enviadas. Então, poderá empacotar várias mensagens antes de enviar o pacote. Se isso ocorrer,
poderá haver atraso ou latência na conexão. Se sua aplicação requer que a latência seja a menor
possível, você deverá configurar a opção DELAY do socket para zero. Ou, se a aplicação pode
conviver com perdas de pacotes ou pacotes desordenados, você pode querer testar uma conexão
UDP ou Datagrama. Conexões UDP também possuem menos sobrecarga de pacotes.

5.1. Parte Cliente

Iremos na primeira parte do projeto construir as classes de comunicação do lado cliente para
proceder à comunicação via sockets.
Classe ClientSocketMidlet.java:
package socket;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ClientSocketMidlet extends MIDlet {

private static Display display;


private boolean isPaused;

public void startApp() {


isPaused = false;
display = Display.getDisplay(this);
Client client = new Client(this);
client.start();
}
public static Display getDisplay() {
return display;
}
public boolean isPaused() {
return isPaused;
}
public void pauseApp() {
isPaused = true;
}
public void destroyApp(boolean unconditional) {
}
}

Classe Client.java:

Desenvolvimento de Aplicações Móveis 10


JEDITM

package socket;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

public class Client implements Runnable, CommandListener {


private ClientSocketMidlet parent;
private Display display;
private Form f;
private StringItem si;
private TextField tf;
private boolean stop;
private Command sendCommand = new Command("Send", Command.ITEM, 1);
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
InputStream is;
OutputStream os;
SocketConnection sc;
Sender sender;

public Client(ClientSocketMidlet m) {
parent = m;
display = Display.getDisplay(parent);
f = new Form("Socket Client");
si = new StringItem("Status:", " ");
tf = new TextField("Send:", "", 30, TextField.ANY);
f.append(si);
f.append(tf);
f.addCommand(exitCommand);
f.addCommand(sendCommand);
f.setCommandListener(this);
display.setCurrent(f);
}
public void start() {
Thread t = new Thread(this);
t.start();
}
public void run() {
try {
sc = (SocketConnection) Connector.open("socket://localhost:5000");
si.setText("Connected to server");
is = sc.openInputStream();
os = sc.openOutputStream();
sender = new Sender(os);
while (true) {
StringBuffer sb = new StringBuffer();
int c = 0;
while (((c = is.read()) != '\n') && (c != -1)) {
sb.append((char) c);
}
if (c == -1) {
break;
}
si.setText("Message received - " + sb.toString());
}
stop();
si.setText("Connection closed");
f.removeCommand(sendCommand);
} catch (ConnectionNotFoundException cnfe) {
Alert a = new Alert("Client",
"Please run Server MIDlet first", null, AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
a.setCommandListener(this);
display.setCurrent(a);
} catch (IOException ioe) {
if (!stop) {

Desenvolvimento de Aplicações Móveis 11


JEDITM

ioe.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void commandAction(Command c, Displayable s) {
if ((c == sendCommand) && !parent.isPaused()) {
sender.send(tf.getString());
}
if ((c == Alert.DISMISS_COMMAND) || (c == exitCommand)) {
parent.notifyDestroyed();
parent.destroyApp(true);
}
}
public void stop() {
try {
stop = true;
if (sender != null) {
sender.stop();
}
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
if (sc != null) {
sc.close();
}
} catch (IOException ioe) {
}
}
}

Classe Sender.java:
package socket;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

public class Sender extends Thread {


private OutputStream os;
private String message;

public Sender(OutputStream os) {


this.os = os;
start();
}
public synchronized void send(String msg) {
message = msg;
notify();
}
public synchronized void run() {
while (true) {
if (message == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
if (message == null) {
break;
}

Desenvolvimento de Aplicações Móveis 12


JEDITM

try {
os.write(message.getBytes());
os.write("\r\n".getBytes());
} catch (IOException ioe) {
ioe.printStackTrace();
}
message = null;
}
}
public synchronized void stop() {
message = null;
notify();
}
}

Desenvolvimento de Aplicações Móveis 13


JEDITM

6. Server Sockets
No modelo cliente-servidor, o servidor continuamente espera pela conexão de um cliente que
deverá conhecer o número da porta deste. Devemos utilizar o método Connector.open() para criar
uma conexão do tipo socket. A URL passada para o método possui um formato semelhante ao um
Socket TCP, com um hostname passado em branco (isto é, socket://:5000).

6.1. Parte Servidora

Iremos agora complementar as classes de comunicação via socket criando as classes que
resolverão a parte do servidor.
Classe ServerSocketMidlet.java:
package socket;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ServerSocketMidlet extends MIDlet {


private static Display display;
private boolean isPaused;

public void startApp() {


isPaused = false;
display = Display.getDisplay(this);
Server server = new Server(this);
server.start();
}
public static Display getDisplay() {
return display;
}
public boolean isPaused() {
return isPaused;
}
public void pauseApp() {
isPaused = true;
}
public void destroyApp(boolean unconditional) {
}
}

Classe Server.java:
package socket;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

public class Server implements Runnable, CommandListener {


private ServerSocketMidlet parent;
private Display display;
private Form f;
private StringItem si;
private TextField tf;
private boolean stop;
private Command sendCommand = new Command("Send", Command.ITEM, 1);
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
InputStream is;
OutputStream os;
SocketConnection sc;
ServerSocketConnection scn;
Sender sender;

Desenvolvimento de Aplicações Móveis 14


JEDITM

public Server(ServerSocketMidlet m) {
parent = m;
display = Display.getDisplay(parent);
f = new Form("Socket Server");
si = new StringItem("Status:", " ");
tf = new TextField("Send:", "", 30, TextField.ANY);
f.append(si);
f.append(tf);
f.addCommand(exitCommand);
f.setCommandListener(this);
display.setCurrent(f);
}
public void start() {
Thread t = new Thread(this);
t.start();
}
public void run() {
try {
si.setText("Waiting for connection");
scn = (ServerSocketConnection) Connector.open("socket://:5000");
sc = (SocketConnection) scn.acceptAndOpen();
si.setText("Connection accepted");
is = sc.openInputStream();
os = sc.openOutputStream();
sender = new Sender(os);
f.addCommand(sendCommand);
while (true) {
StringBuffer sb = new StringBuffer();
int c = 0;
while (((c = is.read()) != '\n') && (c != -1)) {
sb.append((char) c);
}
if (c == -1) {
break;
}
si.setText("Message received - " + sb.toString());
}
stop();
si.setText("Connection is closed");
f.removeCommand(sendCommand);
} catch (IOException ioe) {
if (ioe.getMessage().equals("ServerSocket Open")) {
Alert a = new Alert("Server", "Port 5000 is already taken.", null,
AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
a.setCommandListener(this);
display.setCurrent(a);
} else {
if (!stop) {
ioe.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void commandAction(Command c, Displayable s) {
if ((c == sendCommand) && !parent.isPaused()) {
sender.send(tf.getString());
}
if ((c == Alert.DISMISS_COMMAND) || (c == exitCommand)) {
parent.notifyDestroyed();
parent.destroyApp(true);
}
}
public void stop() {

Desenvolvimento de Aplicações Móveis 15


JEDITM

try {
stop = true;
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
if (sc != null) {
sc.close();
}
if (scn != null) {
scn.close();
}
} catch (IOException ioe) {
}
}
}

Desenvolvimento de Aplicações Móveis 16


JEDITM

7. Datagramas
Conexões por soquetes TCP são seguras. Ao contrário, entregas de pacotes UDP não são
garantidas. Não há garantias de que pacotes enviados utilizando conexões por datagramas sejam
recebidas por quem as solicitou. A ordem em que os pacotes são recebidos não é segura. A ordem
em que os pacotes são enviados não é a mesma ordem em que são recebidos.
Datagramas ou pacotes UDP são utilizados quando a aplicação pode se sustentar (continuar em
operação) mesmo quando um pacote está perdido ou quando não são entregues em ordem.

7.1. Parte Servidora

Iniciaremos o projeto de comunicação por datagramas através da parte servidora.


Classe ServerDatagramMidlet.java:
package datagram;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ServerDatagramMidlet extends MIDlet {


private static Display display;
private boolean isPaused;

public void startApp() {


isPaused = false;
display = Display.getDisplay(this);
Server server = new Server(this);
server.start();
}
public static Display getDisplay() {
return display;
}
public boolean isPaused() {
return isPaused;
}
public void pauseApp() {
isPaused = true;
}
public void destroyApp(boolean unconditional) {
}
}

Classe Server.java:
package datagram;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

public class Server implements Runnable, CommandListener {


private ServerDatagramMidlet parent;
private Display display;
private Form f;
private StringItem si;
private TextField tf;
private Command sendCommand = new Command("Send", Command.ITEM, 1);
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
private Sender sender;
private String address;

public Server(ServerDatagramMidlet m) {

Desenvolvimento de Aplicações Móveis 17


JEDITM

parent = m;
display = Display.getDisplay(parent);
f = new Form("Datagram Server");
si = new StringItem("Status:", " ");
tf = new TextField("Send:", "", 30, TextField.ANY);
f.append(si);
f.append(tf);
f.addCommand(sendCommand);
f.addCommand(exitCommand);
f.setCommandListener(this);
display.setCurrent(f);
}
public void start() {
Thread t = new Thread(this);
t.start();
}
public void run() {
try {
si.setText("Waiting for connection");
DatagramConnection dc = (DatagramConnection)
Connector.open("datagram://:5555");
sender = new Sender(dc);
while (true) {
Datagram dg = dc.newDatagram(100);
dc.receive(dg);
address = dg.getAddress();
si.setText("Message received - " + new String(dg.getData(), 0,
dg.getLength()));
}
} catch (IOException ioe) {
Alert a = new Alert("Server", "Port 5000 is already taken.", null,
AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
a.setCommandListener(this);
display.setCurrent(a);
} catch (Exception e) {
e.printStackTrace();
}
}
public void commandAction(Command c, Displayable s) {
if ((c == sendCommand) && !parent.isPaused()) {
if (address == null) {
si.setText("No destination address");
} else {
sender.send(address, tf.getString());
}
}
if ((c == Alert.DISMISS_COMMAND) || (c == exitCommand)) {
parent.destroyApp(true);
parent.notifyDestroyed();
}
}
}

7.2. Classe de Comunicação

Esta classe é aproveitada tanto pelo lado servidor quanto pelo lado cliente para o controle do
envio das mensagens.
Classe Sender.java:
package datagram;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;

Desenvolvimento de Aplicações Móveis 18


JEDITM

import javax.microedition.midlet.*;

public class Sender extends Thread {


private DatagramConnection dc;
private String address;
private String message;

public Sender(DatagramConnection dc) {


this.dc = dc;
start();
}
public synchronized void send(String addr, String msg) {
address = addr;
message = msg;
notify();
}
public synchronized void run() {
while (true) {
if (message == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
try {
byte[] bytes = message.getBytes();
Datagram dg = null;
if (address == null) {
dg = dc.newDatagram(bytes, bytes.length);
} else {
dg = dc.newDatagram(bytes, bytes.length, address);
}
dc.send(dg);
} catch (Exception ioe) {
ioe.printStackTrace();
}
message = null;
}
}
}

7.3. Parte Cliente

Complementaremos o projeto com as classes que definem a parte cliente.


Classe ClientDatagramMidlet.java
package datagram;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class ClientDatagramMidlet extends MIDlet {


private static Display display;
private boolean isPaused;

public void startApp() {


isPaused = false;
display = Display.getDisplay(this);
Client client = new Client(this);
client.start();
}
public static Display getDisplay() {
return display;
}
public boolean isPaused() {

Desenvolvimento de Aplicações Móveis 19


JEDITM

return isPaused;
}
public void pauseApp() {
isPaused = true;
}
public void destroyApp(boolean unconditional) {
}
}

Classe Client.java:
package datagram;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

public class Client implements Runnable, CommandListener {

private ClientDatagramMidlet parent;


private Display display;
private Form f;
private StringItem si;
private TextField tf;
private Command sendCommand = new Command("Send", Command.ITEM, 1);
private Command exitCommand = new Command("Exit", Command.EXIT, 1);
private Sender sender;

public Client(ClientDatagramMidlet m) {
parent = m;
display = Display.getDisplay(parent);
f = new Form("Datagram Client");
si = new StringItem("Status:", " ");
tf = new TextField("Send:", "", 30, TextField.ANY);
f.append(si);
f.append(tf);
f.addCommand(sendCommand);
f.addCommand(exitCommand);
f.setCommandListener(this);
display.setCurrent(f);
}
public void start() {
Thread t = new Thread(this);
t.start();
}
public void run() {
try {
DatagramConnection dc = (DatagramConnection) Connector.open(
"datagram://localhost:5555");
si.setText("Connected to server");
sender = new Sender(dc);
while (true) {
Datagram dg = dc.newDatagram(100);
dc.receive(dg);
if (dg.getLength() > 0) {
si.setText("Message received - " + new String(dg.getData(), 0,
dg.getLength()));
}
}
} catch (ConnectionNotFoundException cnfe) {
Alert a = new Alert("Client", "Please run Server MIDlet first", null,
AlertType.ERROR);
a.setTimeout(Alert.FOREVER);
display.setCurrent(a);
} catch (IOException ioe) {
ioe.printStackTrace();

Desenvolvimento de Aplicações Móveis 20


JEDITM

}
}
public void commandAction(Command c, Displayable s) {
if ((c == sendCommand) && !parent.isPaused()) {
sender.send(null, tf.getString());
}
if (c == exitCommand) {
parent.destroyApp(true);
parent.notifyDestroyed();
}
}
}

Desenvolvimento de Aplicações Móveis 21


JEDITM

8. Exercícios
8.1. Buscar dados da URL

Criar um MIDlet que detalhe um endereço HTTP. Pesquisar a URL utilizando o método GET e
mostrar as propriedades desta conexão (caso esteja disponível), tais como: código da resposta,
descrição da mensagem, tamanho, tipo, modo de codificação, se está finalizado e data da última
modificação.

Desenvolvimento de Aplicações Móveis 22


Módulo 5
Desenvolvimento de Aplicações Móveis

Lição 7
Comunicação Corporativa

Versão 1.0 - Set/2007


JEDITM

Autor Necessidades para os Exercícios


A. Oliver de Guzman Sistemas Operacionais Suportados
NetBeans IDE 5.5 para os seguintes sistemas operacionais:
• Microsoft Windows XP Profissional SP2 ou superior
• Mac OS X 10.4.5 ou superior
Equipe • Red Hat Fedora Core 3
Rommel Feria • Solaris™ 10 Operating System (SPARC® e x86/x64 Platform Edition)
John Paul Petines NetBeans Enterprise Pack, poderá ser executado nas seguintes plataformas:
• Microsoft Windows 2000 Profissional SP4
• Solaris™ 8 OS (SPARC e x86/x64 Platform Edition) e Solaris 9 OS (SPARC e
x86/x64 Platform Edition)
• Várias outras distribuições Linux

Configuração Mínima de Hardware


Nota: IDE NetBeans com resolução de tela em 1024x768 pixel
Sistema Operacional Processador Memória HD Livre
Microsoft Windows 500 MHz Intel Pentium III 512 MB 850 MB
workstation ou equivalente
Linux 500 MHz Intel Pentium III 512 MB 450 MB
workstation ou equivalente
Solaris OS (SPARC) UltraSPARC II 450 MHz 512 MB 450 MB
Solaris OS (x86/x64 AMD Opteron 100 Série 1.8 GHz 512 MB 450 MB
Platform Edition)
Mac OS X PowerPC G4 512 MB 450 MB

Configuração Recomendada de Hardware

Sistema Operacional Processador Memória HD Livre


Microsoft Windows 1.4 GHz Intel Pentium III 1 GB 1 GB
workstation ou equivalente
Linux 1.4 GHz Intel Pentium III 1 GB 850 MB
workstation ou equivalente
Solaris OS (SPARC) UltraSPARC IIIi 1 GHz 1 GB 850 MB
Solaris OS (x86/x64 AMD Opteron 100 Series 1.8 GHz 1 GB 850 MB
Platform Edition)
Mac OS X PowerPC G5 1 GB 850 MB

Requerimentos de Software
NetBeans Enterprise Pack 5.5 executando sobre Java 2 Platform Standard Edition
Development Kit 5.0 ou superior (JDK 5.0, versão 1.5.0_01 ou superior), contemplando
a Java Runtime Environment, ferramentas de desenvolvimento para compilar, depurar,
e executar aplicações escritas em linguagem Java. Sun Java System Application Server
Platform Edition 9.
• Para Solaris, Windows, e Linux, os arquivos da JDK podem ser obtidos para
sua plataforma em http://java.sun.com/j2se/1.5.0/download.html
• Para Mac OS X, Java 2 Plataform Standard Edition (J2SE) 5.0 Release 4, pode
ser obtida diretamente da Apple's Developer Connection, no endereço: http://
developer.apple.com/java (é necessário registrar o download da JDK).

Para mais informações: http://www.netbeans.org/community/releases/55/relnotes.html

Desenvolvimento de Aplicações Móveis 2


JEDITM

Colaboradores que auxiliaram no processo de tradução e revisão


Aécio Júnior Fábio Bombonato Luiz Fernandes de Oliveira Junior
Alexandre Mori Fabrício Ribeiro Brigagão Marco Aurélio Martins Bessa
Alexis da Rocha Silva Francisco das Chagas Maria Carolina Ferreira da Silva
Allan Souza Nunes Frederico Dubiel Massimiliano Giroldi
Allan Wojcik da Silva Herivelto Gabriel dos Santos Mauro Cardoso Mortoni
Anderson Moreira Paiva Jacqueline Susann Barbosa Paulo Afonso Corrêa
Andre Neves de Amorim João Vianney Barrozo Costa Paulo Oliveira Sampaio Reis
Angelo de Oliveira Kefreen Ryenz Batista Lacerda Pedro Henrique Pereira de Andrade
Antonio Jose R. Alves Ramos Kleberth Bezerra G. dos Santos Ronie Dotzlaw
Aurélio Soares Neto Leandro Silva de Morais Seire Pareja
Bruno da Silva Bonfim Leonardo Ribas Segala Sergio Terzella
Carlos Fernando Gonçalves Lucas Vinícius Bibiano Thomé Vanessa dos Santos Almeida
Denis Mitsuo Nakasaki Luciana Rocha de Oliveira Robson Alves Macêdo

Auxiliadores especiais

Revisão Geral do texto para os seguintes Países:


• Brasil – Tiago Flach
• Guiné Bissau – Alfredo Cá, Bunene Sisse e Buon Olossato Quebi – ONG Asas de Socorro

Coordenação do DFJUG

• Daniel deOliveira – JUGLeader responsável pelos acordos de parcerias


• Luci Campos - Idealizadora do DFJUG responsável pelo apoio social
• Fernando Anselmo - Coordenador responsável pelo processo de tradução e revisão,
disponibilização dos materiais e inserção de novos módulos
• Rodrigo Nunes - Coordenador responsável pela parte multimídia
• Sérgio Gomes Veloso - Coordenador responsável pelo ambiente JEDITM (Moodle)

Agradecimento Especial
John Paul Petines – Criador da Iniciativa JEDITM
Rommel Feria – Criador da Iniciativa JEDITM

Desenvolvimento de Aplicações Móveis 3


JEDITM

1. Objetivos
Nesta lição, aprenderemos a escrever Servlets, utilizar páginas com JSP e JSTL, acessar um banco
de dados através de JDBC e a criar e ler documentos XML.

Ao final desta lição, o estudante será capaz de:


• Escrever Servlets simples
• Escrever Java Server Pages (JSP) simples utilizando JSTL
• Escrever código JSTL utilizando Expression Language (EL)
• Acessar banco de dados utilizando a tecnologia JDBC
• Gerar XML
• Ler um arquivo XML no cliente móvel

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. Servlets
Servlet é uma classe Java que é executada no servidor WEB para implementar transações do tipo
requisição-resposta (request-response). Através dos servlet, a tecnologia Java fornece uma
alternativa melhor e mais portável do que scripts CGI (Common Gateway Interface). Comparado
com scripts CGI (escritos em Perl, scripts sheel, ou outras linguagens de script), servlet fornece
uma tecnologia escalável, independente de plataforma, para entregar conteúdo dinâmico.
Um servlet tem dois métodos para processar uma requisição, os métodos doGet() e doPost().
Esses métodos são chamados quando um cliente WEB (browser) envia um comando "GET" ou
"POST". Esses métodos têm parâmetros idênticos. Esses parâmetros são normalmente entregues
para um método comum. Portanto, tanto a requisição GET quanto a requisição POST são
manipuladas da mesma maneira.
protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// requisição de processamento ...
}

protected void doPost(


HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// requisição de processamento ...
}

A seguir teremos um servlet completo:


import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SampleServlet extends HttpServlet {

protected void processRequest(


HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet SampleServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Servlet SampleServlet at "
+ request.getContextPath () + "</h1>");
out.println("</body>");
out.println("</html>");
out.close();
}
protected void doGet(
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/** Retorna uma breve descrição do servlet.
*/

Desenvolvimento de Aplicações Móveis 5


JEDITM

public String getServletInfo() {


return "Short description";
}
}

Para criar um novo Projeto WEB (Web Project) no NetBeans, selecione a partir do menu principal
a opção File -> New Project...

Figura 1: Janela New Project do NetBeans

Na janela acima selecione em Categories a opção Web e em Projects selecione a opção Web
Application.

Desenvolvimento de Aplicações Móveis 6


JEDITM

Figura 2: Janela New Web Application do NetBeans

Na janela acima informe o nome para o projeto e pressione o botão Finish. Após o projeto criado
poderemos criar um novo arquivo tipo servlet. Para o mesmo. Para criar este arquivo selecione, a
partir do menu principal, a opção em File -> New File...

Figura 3: Janela New File do NetBeans

Na janela acima selecione em Categories a opção Web e em File Types selecione a opção Servlet.

Figura 4: Janela New Servlet do NetBeans

Na janela acima informe o nome para o servlet e pressione o botão Finish.

Desenvolvimento de Aplicações Móveis 7


JEDITM

3. JSP e JSTL
O objetivo primário das Java Server Pages (JSP) e Standard Tag Library (JSTL) é ajudar aos
desenvolvedores simplificar a escrita das de páginas WEB. JSTL faz a ponte entre programadores
e autores (não programadores) fornecendo uma linguagem de expressão simples para a
construção de páginas JSP.
Além do suporte às ações da linguagem de expressão e de fluxo de controle, JSTL fornece
também funcionalidade para acessar recursos baseados em URL, internacionalização e formatação
de números e datas, acesso de base de dados e processamento de XML.
JTSL é composta de diversas bibliotecas de tags, agrupadas com base na área funcional.

Área Prefixo URI Exemplo de código


core c http://java.sun.com/jstl/core <c:out value="${var}"/>
I18N formatting fmt http://java.sun.com/jstl/fmt <fmt:formatNumber
XML processing x http://java.sun.com/jstl/xml <x:forEach ...
Database (SQL) sql http://java.sun.com/jstl/sql <sql:query var=...
Functions fn http://java.sun.com/jstl/functions <fn:

Nesta seção, abordaremos o uso da biblioteca core.

3.1. Configuração

Para usar as tags JSTL, a diretiva da taglib deve ser incluída na página JSP, uma para cada área
funcional que será usada na página.
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>

Figura 5: Código JSP com a tag core

Deve-se incluir também o jstl.jar no diretório de bibliotecas do projeto:


1. Pressionar o botão direito do mouse em cima do diretório de bibliotecas do projeto e
selecionar "Add Library" no menu pop-up:

Figura 6: Adicionando uma biblioteca ao projeto

Desenvolvimento de Aplicações Móveis 8


JEDITM

2. Selecionar "JSTL 1.1" e selecionar a opção "Add Library". "JSTL 1.1 – standard.jar" e
"STL 1.1 – jstl.jar" serão adicionados à biblioteca do projeto.

Figura 7: Adicionando uma biblioteca ao projeto

3.2. Hello, world!

Uma página JSP, ao contrário de um servlet, é uma página HTML com tags que permitem a
programação Java. A seguir temos uma página JSP:
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"


"http://www.w3.org/TR/html4/loose.dtd">
<c:set var="mensagem" value="hello, world!"/>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><c:out value="${mensagem}"/></title>
</head>
<body>
<h1><c:out value="${mensagem}"/></h1>
</body>
</html>

Seguindo as mesmas instruções para gerar um servlet, entretanto selecione JSP e indique o nome
como hello.jsp. Para ver a saída desta página, pressionar o botão direito do mouse em cima do
nome do arquivo e selecionar a opção "Run File" é mostrado o seguinte resultado no seu
navegador web:

Figura 8: Resultado da execução da página JSP

Desenvolvimento de Aplicações Móveis 9


JEDITM

3.3. Transferindo controle do Servlet para JSP


import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SampleServlet2 extends HttpServlet {

protected void processRequest(


HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String[] days = {"Sunday", "Monday", "Tuesday", "Wednesday"};
request.setAttribute("days", days);
request.setAttribute("message", "hello, world!");
// Transfere o controle para a página JSP
RequestDispatcher dispatcher = request.
getRequestDispatcher("/hello.jsp");
if (dispatcher != null)
dispatcher.forward(request, response);
}
protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}
public String getServletInfo() {
return "Short description";
}
}

Figura 9: Resultado da execução do Servlet

Desenvolvimento de Aplicações Móveis 10


JEDITM

3.4. Linguagem de Expressão

3.4.1. Acessando Atributos, Propriedades e Coleções

Para acessar um atributo, a sintaxe é: ${var}


Exemplo: saída do valor do atributo username
<c:out value="${username}"/>

JSTL unifica o acesso às propriedades do JavaBean e valores de coleção. A expressão varX.varY é


equivalente para varX[varY] em JSTL. A expressão varX.varY (ou varX[varY]) será avaliada,
dependendo do tipo de varX:
1. Se varX é um JavaBean, varY será convertido em cadeia de caractere. Se varY é uma
propriedade de leitura de varX, ela pode retornar o resultado da chamada ao método de
acesso: varX.getVarY()
2. Se varX é uma coleção do tipo List: varY é forçada para int. Converter para: varX.get(varY)
3. Se varX é uma coleção do tipo Vector: varY é forçada para int. Converter para:
Array.get(varX, varY)
4. Se varX é uma coleção do tipo Map: Converter para: varX.get(varY)
Os atributos podem ter escopo de página, de requisição e da aplicação. O linguagem de expressão
procuraria pelo identificador nestes escopos. Se o identificador não for achado, é retornado nulo.

3.4.2. Objetos Implícitos

JSTL inicializa automaticamente diversos atributos de mapeamento com valores de origens


diferentes. Estes atributos estão disponíveis para as páginas JSP sem qualquer inicialização do
usuário. Por exemplo, o atributo “param” contém mapeamentos de nomes de parâmetros e
valores de requisição. Estes nomes e valores (param) vêm de formulários HTML, através da de
submissão de métodos HTTP GET ou POST.
Objeto Conteúdo
Implícito
Contém um mapeamento de nomes de atributos de escopo de página para seus
pageScope
valores
Contém um mapeamento de nomes de atributos de escopo de requisição para seus
requestScope
valores
Contém um mapeamento de nomes de atributos de escopo de sessão para seus
sessionScope
valores
Contém um mapeamento de nomes de atributos de escopo de aplicação para seus
applicationScope
valores
Contém um mapeamento de nomes de parâmetros para seus valores de parâmetros
param
(cadeia de caracteres). Equivalente a ServletRequest.getParameter(String)
Contém um mapeamento de nomes de cabeçalho para seus valores (cadeia de
header
caracteres). Equivalente a servletRequst.getHeader(String)
Contém contendo um mapeamento de nomes de "cookies" para seus valores.
cookie
Equivalente a HttpServletRequest.getCookie(String)

3.4.3. Operadores

A linguagem da expressão (EL) da JSTL suporta operadores relacionais, aritméticos e lógicos. Os


operadores relacionais suportados são:
● == ou eq
● != ou ne
● < ou lt

Desenvolvimento de Aplicações Móveis 11


JEDITM

● > ou gt
● <= ou le
● >= ou ge
Os operadores lógicos suportados são:
● && ou and
● || ou or
● ! ou not
E os operadores aritméticos suportados são:
● + (adição)
● - (subtração)
● * (multiplicação)
● / (divisão)
● % ou mod (resto da divisão ou módulo)
O operador adicional empty é muito útil para testar valores nulos ou vazios.
<c:if test="${empty param.username}">No username</c:if>

3.4.4. Exceções a Valores Padrões (Básicos)

Para simplificar páginas de JSP, os erros simples não gerarão exceções. Indicar um atributo com
um valor nulo indicará simplesmente “(zero)” em vez de gerar um NullPointerException.
Username: <input
type="text"
value="<c:out value="${param.username}"/>"
name="username" />

Qualquer atributo não definido que for utilizado em expressões terá seu valor padrão como 0
(zero). Esta expressão retornaria o valor 1, se o parâmetro “start” não fosse inicializado:
<c:out value="${param.start + 1}"/>

3.5. Biblioteca Core

3.5.1. Tag <c:out>

O tag <c:out> avalia uma expressão e retorna o seu resultado.


Sintaxe :
<c:out
value="value"
[escapeXml="{true|false}"]
[default="defaultValue"]
/>

Nome Dinâmico Requisito Tipo Descrição


A expressão a ser avaliada
value sim sim Objeto
Se verdadeiro, os caracteres <, >, &, ' e " são
convertidos em seus códigos da entidade do caráter
escapeXml sim não boolean
(por exemplo: > conversos ao &gt;). O valor padrão é
true.
default sim não Objeto O valor padrão se o valor do resultado for nulo

Exemplos:

Desenvolvimento de Aplicações Móveis 12


JEDITM

Rows: <c:out value="${param.numRows}" defaultValue="20" />


Description:
<pre>
<c:out value="${bookmark.description}" escapeXml="false" />
</pre>

3.5.2. Tag <c:set>

Ajusta o valor de um atributo em um determinado escopo.


Sintaxe:
<c:set
value="value"
var="varName"
[scope="{page|request|session|application}"]
/>

Nome Dinâmico Requisito Tipo Descrição


value sim sim Objeto A expressão a ser avaliada
O nome do atributo exportada que conterá o valor da
var não sim String expressão. O tipo do atributo segue o tipo do
resultado da expressão
scope não não String O escopo do atributo

Exemplo:
<c:set var="fullName" value="${lastName, firstName}" />

3.5.3. Tag <c:remove>

Esta tag remove um atributo de um determinado escopo.


Sintaxe:
<c:remove
var="varName"
[scope="{page|request|session|application}"]
/>

Nome Dinâmico Requisito Tipo Descrição


var não sim String O nome do atributo a ser eliminado
scope não não String O escopo do atributo

3.5.4. Tag <c:if>

Realiza uma avaliação condicional. O conteúdo do corpo será processado se o teste de avaliação
da condição informada for verdadeiro.
Sintaxe:
<c:if test="testCondition"
[var="varName"] [scope="{page|request|session|application}"]>
body content
</c:if>

ou
<c:if test="testCondition"
var="varName" [scope="{page|request|session|application}"]
/>

Desenvolvimento de Aplicações Móveis 13


JEDITM

Nome Dinâmico Requerido Tipo Descrição


A condição testada que determina se o conteúdo do
test sim sim boolean
corpo deverá ser processado
Nome do atributo exportado que irá conter o valor da
var não não/sim String condição testada. O tipo do atributo de escopo é
Boolean
scope não não String Escopo do atributo

Exemplo:
<c:if test="${empty param.username}">No username</c:if>

3.5.5. Tag <c:choose><c:when><c:otherwise>

A tag <c:choose> é uma substituta para a instrução if-else-if do Java, permitindo a execução
condicional mutuamente exclusiva. O conteúdo do corpo composto pela tag <c:otherwise> será
avaliado se nenhuma instrução das tags <c:when> for considerada verdadeira. O bloco
<c:otherwise> deverá ser o último da instrução e também ser precedido por no mínimo uma tag
do tipo <c:when>.
Sintaxe:
<c:choose>
<c:when test="condition1">
instruções para esta condição
</c:when>
<c:when test="condition2">
instruções para esta condição
</c:when>...
<c:otherwise>
instruções caso nenhuma condição tenha sido considerada verdadeira
</c:otherwise>
</c:choose>

Nome Dinâmico Requerido Tipo Descrição


A condição de teste que determina se o conteúdo
test sim sim boolean
do corpo deverá ser processado

Exemplo:
<c:choose>
<c:when test="${gender eq 'M'}">Male</c:when>
<c:when test="${gender eq 'F'}">Female</c:when>
<c:otherwise>Unknown</c:otherwise>
</c:choose>

3.5.6. Tag <c:forEach>

A tag <c:forEach> realiza iteração sobre o conteúdo de uma coleção. A coleção pode ser qualquer
uma das subclasses de java.util.Collection e java.util.Map. Arrays de objetos e tipos primitivos
também são suportados. Uma String com valores separados por vírgula ("True,False") também
pode ser utilizada para o processo. Enquanto houver itens na coleção o conteúdo do corpo será
processado.
A tag também poderá ser utilizada para iterações com número fixo de repetições.
O atributo varStatus é do tipo javax.servlet.jsp.jstl.core.LoopTagStatus e tem a propriedade index
(índice da iteração atual, iniciado em zero) e count (a contagem da iteração atual, iniciada em 1
(um), por exemplo, se o índice inicial é 20, o índice final é 80 e o intervalo é 10, o count poderá
ser 1,2,3,4,5,6,7). As propriedades lógicas first e last indicam se a iteração atual é a primeira ou
a última, respectivamente. Existem também as propriedades begin, end e step que guardam os

Desenvolvimento de Aplicações Móveis 14


JEDITM

valores dos argumentos inicial, final e do passo realizado pelo laço.


Sintaxe:
<c:forEach
[var="varName"]
items="collection"
[varStatus="varStatusName"]
[begin="begin"] [end="end"] [step="step"]
>
instruções a serem executadas
</c:forEach>

ou
<c:forEach [var="varName"]
[varStatus="varStatusName"]
begin="begin" end="end" [step="step"]
>
instruções a serem executadas
</c:forEach>

Nome Dinâmico Requerido Tipo Descrição


O nome de um atributo exportado para cada
var não não variável
elemento de uma coleção
Collection,
items sim sim/não Map, Array, Coleção de itens para realizar interações
String
O nome do atributo exportado para a situação da
varStatus não não String operação. O tipo do objeto exportado é
javax.servlet.jsp.jstl.core.LoopTagStatus
Índice inicial (base zero) na coleção, ou um índice
begin sim não/sim int
inicial fixo (se a coleção não for especificada)
Índice final (base zero) na coleção, ou um índice
end sim não/sim int
final fixo (se a coleção não for especificada)
O laço será processado por um intervalo definido de
step sim não int
itens da iteração

Exemplo:
<select name="gender">
<c:forEach var="gender" items="${genderList}">
<option value="<c:out value="${gender.code}"/>">
<c:out value="${gender.name}"/>
</option>
</c:forEach>
</table>

3.5.7. Tag <c:forTokens>

Esta tag realiza iteração sobre os segmentos de texto que devem ser separados por um
delimitador em um objeto do tipo String.
Sintaxe:
<c:forTokens
items="stringOfTokens"
delims="delimiters"
[var="varName"]
[varStatus="varStatusName"]
[begin="begin"] [end="end"] [step="step"]
>
instruções a serem executadas

Desenvolvimento de Aplicações Móveis 15


JEDITM

</c:forTokens >

Nome Dinâmico Requerido Tipo Descrição


O nome do atributo exportado para toda instrução
var não não String
iteração e símbolo
items sim sim String Literal de símbolos
Delimitadores, caracteres que separam os símbolos no
delims sim sim String
literal de itens
O nome do atributo exportado para o status da
varStatus não não String operação. O objeto exportado é do tipo
javax.servlet.jsp.jstl.core.LoopTagStatus
Índice do começo (base-zero) da iteração. Se não for
begin sim não int especificado, a iteração vai começar com o primeiro
símbolo.
end sim não int Final do índice da iteração
O laço será processado por um intervalo definido de
step sim não int
itens da iteração

Exemplo:
<select name="gender">
<c:forEach var="gender" items="Male,Female" delims=",">
<option value="<c:out value="${gender}"/>">
<c:out value="${gender}"/>
</option>
</c:forEach>
</table>

Desenvolvimento de Aplicações Móveis 16


JEDITM

4. JDBC
Nessa seção, discutiremos como persistir dados ou objetos. Para essa funcionalidade precisamos
de um banco de dados (relacional). A biblioteca JDBC permite executar consultas e alterações em
um banco de dados. Antes de podermos usar o JDBC, precisamos ter certeza de que três pré-
requisitos estão sendo satisfeitos:
● JDBC library (biblioteca) – incluído no JDK
● O servidor de banco de dados – usaremos o MySQL (www.mysql.com)
● Driver JDBC – vem com o DBMS, instale o jar mysql-connector-java-3.x.x-bin.jar para o
JDBC 3.0

Figura 10: Adicionando as bibliotecas ao projeto

A tabela usada nesses exemplos pode ser recriada usando os comandos SQL CREATE AND
INSERT:
CREATE TABLE `task` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`task` varchar(128) NOT NULL default '',
`duration` int(11) NOT NULL default '0',
`assignedTo` varchar(64) NOT NULL default '',
`status` char(1) NOT NULL default '',
PRIMARY KEY (`id`)
);

INSERT INTO `task`


(`id`, `task`, `duration`, `assignedTo`, `status`)
VALUES
(1,'connect to database',2,'alex','0'),
(2,'list table rows',4,'alex','0'),
(3,'update row',8,'you','0');

4.1. Carregando o Driver

Para utilizar o driver JDBC com um banco de dados em particular temos que carregá-lo utilizando
Class.forName(). O nome do driver é dependente do driver do banco de dados que carregaremos.
Em nosso caso, utilizaremos o mysql jdbc driver:
String driver = "com.mysql.jdbc.Driver";
Class.forName(driver);

4.2. Estabelecendo a Conexão

Para estabelecer uma conexão com o banco de dados precisamos da URL para o banco de dados.
Precisaremos também ter acesso ao banco de dados. Um nome de usuário e senha válidos para o
acesso ao banco de dados serão requeridos.
String url = "jdbc:mysql://localhost:3306/jedi";
String username = "root";

Desenvolvimento de Aplicações Móveis 17


JEDITM

String password = "password";


conn = DriverManager.getConnection(url, username, password);

4.3. Executando consultas SQL

O método executeQuery() retorna um objeto do tipo ResultSet. Para percorrer por todas as linhas
do resultado da consulta utilizaremos o método next(). Existem alguns métodos que retornam as
colunas da linha corrente, cada uma para um tipo de dados específico. Nesse exemplo,
recuperamos atributos dos tipos String e int utilizando getString() e getInt():
Statement statement = conn.createStatement();
String query = "SELECT task,duration,duration FROM task";
ResultSet rs = statement.executeQuery(query);
out.println("<table>");
out.println("<tr>");
out.println("<th>Task</th>");
out.println("<th>Duration</th>");
out.println("<th>Assigned to</th>");
out.println("</tr>");
while (rs.next()) {
String task = rs.getString("task");
int duration = rs.getInt("duration");
String assignedTo = rs.getString("assignedTo");
out.println("<tr>");
out.println("<td>" + task + "</td>");
out.println("<td>" + duration + "</td>");
out.println("<td>" + duration + "</td>");
out.println("</tr>");
}
out.println("</table>");

Figura 11: Resultado da consulta

4.4. Alterando tabelas

Para modificar registros nas tabelas com os comandos INSERT, UPDATE e DELETE (inclusão,
alteração e exclusão, respectivamente), o método executeUpdate() é utlizado.
String task = (String) request.getParameter("task");
String duration = (String) request.getParameter("duration");
String assignedTo = (String) request.getParameter("assignedTo");
String status = (String) request.getParameter("status");
Long id = new Long(idStr);
String updateQuery;
ResultSet rs = dao.query("SELECT id from task WHERE id ='" + id + "'");
if (rs.next()){
// altera a entrada da tarefa
updateQuery = "UPDATE task SET"
+ " task='" + (task != null? task:"") + "'"
+ ",duration='" + (duration != null ? duration:"") + "'"
+ ",assignedTo='" + (assignedTo != null ? assignedTo:"") + "'"
+ ",status='" + (status != null ? status:"") + "'"
+ " WHERE id=" + id;
} else {
// nova entrada da tarefa
updateQuery = "INSERT INTO task (task, duration, assignedTo, status) "
+ "VALUES ("

Desenvolvimento de Aplicações Móveis 18


JEDITM

+ "'" + task + "',"


+ "'" + duration + "',"
+ "'" + assignedTo + "',"
+ "'" + status + "'"
+ ")";
}
statement.executeUpdate(updateQuery);

Desenvolvimento de Aplicações Móveis 19


JEDITM

5. XML
XML, a linguagem de marcação extensível (eXtensible Markup Language), é uma linguagem de
marcação baseada em texto. Com XML, pode-se apresentar dados em um documento estruturado
de texto.
Assim como o HTML, as tags XML são definidas usando os símbolos maior e menor: <>.
Entretanto, diferente do HTML, XML é mais fácil de se analisar. Um documento XML é estruturado
com entidades formando uma estrutura de árvore.
Pode-se utilizar qualquer nome de tag apropriado que seja desejado, desde que todas as
aplicações que utilizam o documento XML utilizem os mesmos nomes de tag. Tags podem conter
atributos. No exemplo abaixo, a primeira "task" (tarefa) tem um atributo "id" (identificador) igual
a "1" enquanto a segunda "task" tem um atributo "id" igual a "2".
<tasks>
<task id="1">
<name>connect to database</name>
<duration>2</duration>
<assignedTo>alex</assignedTo>
<status>0</status>
</task>
<task id="2">
<name>list table rows</name>
<duration>4</duration>
<assignedTo>alex</assignedTo>
<status>4</status>
</task>
</tasks>

5.1. Analisando o XML

Até a data da escrita deste texto, não havia nenhuma biblioteca padrão definida pelo JCP para
análise de XML em CLDC. Entretanto, existem muitas bibliotecas XML que trabalham com a CLDC.
Uma delas é a NanoXML (http://nanoxml.sourceforge.net/). A versão original da NanoXML não
trabalha com a CLDC. O usuário deve baixar a versão modificada que trabalha com CLDC em
http://www.ericgiguere.com/nanoxml e incluí-la no seu projeto móvel sob o nome de pacote
"nanoxml".
import java.io.*;
import java.util.*;
import nanoxml.*;

...
public String[] parseXml(String xml) {
kXMLElement root = new kXMLElement();
try {
root.parseString(xml);
Vector taskList = root.getChildren();
Vector items = new Vector();
for (int i=0; i<taskList.size(); i++){
kXMLElement task = (kXMLElement) taskList.elementAt(i);
String tagName = task.getTagName();
if (tagName != null && tagName.equalsIgnoreCase("task")){
Vector taskProperties = task.getChildren();
String[] fieldNames = {"name", "duration", "assignedTo", "status"};
String[] fields = new String[fieldNames.length];
String id = task.getProperty("id", "0");
for (int j=0; j<taskProperties.size(); j++) {
kXMLElement prop = (kXMLElement) taskProperties.elementAt(j);
String propTagName = prop.getTagName();
if (propTagName != null) {
for (int p=0; p<fieldNames.length; p++) {
if (propTagName.equalsIgnoreCase(fieldNames[p])) {

Desenvolvimento de Aplicações Móveis 20


JEDITM

fields[p] = prop.getContents();
}
}
}
}
items.addElement(id + ": " + fields[0]);
}
}
String[] itemArr = new String[items.size()];
items.copyInto(itemArr);
return itemArr;
} catch( kXMLParseException ke ){
return(null);
}
}

Desenvolvimento de Aplicações Móveis 21


JEDITM

6. Exercícios
6.1. Cabeçalhos das tabelas com linhas de cores alternadas

Escrever um código JSTL que irá interagir com o mapa implícito "header" e mostrar o header
chave/nome e o valor em uma tabela HTML. Linhas de número ímpar tem um fundo na cor
lightyellow (<tr bgcolor="lightyellow">...). Note que o índice varStatus começa em zero. Note
que o (header) Map tem as propriedades "key" e "value".
Saída do exemplo:

accept-encoding gzip,deflate
connection keep-alive
accept-language en-us,en;q=0.5
host localhost:8084
accept-charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Mozilla/5.0 (Linux; U; Linux v0.99; en-US) Gecko/20050511 Firefox/
user-agent
1.2.3
text/xml,application/xml,application/xhtml
accept
+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
keep-alive 300

6.2. Servlets e JSP

Criar um servlet e uma aplicação JSTL que deve mostrar no formato XML em um array de objetos.
Os atributos do objeto são: nome e endereço IP. A classe Java deve parecer com esta:
public class Host {
private String name;
private String ip;
public Host(String name, String ip) {
this.name = name;
this.ip = ip;
}
public String getName(){ return(name); }
public String getIp(){ return(ip); }
public void setName(String name){ this.name = name; }
public void setIp(String ip){ this.ip = ip; }
}

Deve se passar um array estático dos Hosts no Servlet para o JSP usando o método
request.setAttribute().
Host[] hosts = {
new Host("localhost", "127.0.0.1"), new Host("java.sun.com", "1.2.3.4")};

A saída em XML se deve parecer como esta:


<hosts>
<host name="localhost">
<ip>127.0.0.1</ip>
</host>
<host name="java.sun.com">
<ip>1.2.3.4</ip>
</host>
</hosts>

Desenvolvimento de Aplicações Móveis 22


Módulo 5
Desenvolvimento de Aplicações Móveis

Lição 8
Otimizações

Versão 1.0 - Set/2007


JEDITM

1. Objetivos
Antes de realmente fazer qualquer otimização em seus programas, deve-se certificar de que o
pacote de software é de boa qualidade. A execução das otimizações nas classes do projeto deve
ser deixado por último. Algumas técnicas discutidas nesta lição são úteis para evitar alguns erros
de programação.
Ao final desta lição, o estudante será capaz de:
• Utilizar as diferentes técnicas de otimização em aplicações móveis.

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. Execução de Classes
2.1. Utilizar StringBuffer ao invés de String

Deve-se recordar que em Java Strings são objetos imutáveis. Usando-se os métodos de String
criamos objetos individuais da String. A simples concatenação de Strings cria múltiplos objetos de
Strings (a menos que as Strings sejam constantes e o compilador seja aguçado o suficiente para
concatená-las durante sua compilação). O uso da StringBuffer não apenas otimiza o tempo de
execução de suas classes (menos tempo de execução na criação de objetos), como também irá
otimizar o uso da memória (menos Strings para alocar).

String StringBuffer
String a, b, c; String a, b, c;
... ...
String message = StringBuffer message = new StringBuffer(255);
"a=" + a + "\n" message.append("a=");
+ "b=" + b + "\n" message.append(a);
+ "c=" + c + "\n"; message.append("\n");
message.append("b=");
message.append(b);
message.append("\n");
message.append("c=");
message.append(c);
message.append("\n");

2.2. Utilizar uma área de clipping ao desenhar

Usar Graphics.setClip() reduz o tempo de execução porque se está desenhando, somente, o


número correto de pixels na tela. Lembre-se que gráficos desenhados na tela consomem muito
tempo de execução. Reduzir o número de pixels a serem desenhados melhora o desempenho de
seu programa durante a execução.
Graphics g;
int x1, y1, x2, y2;
...
g.setClip(x1, y1, x2, y2);
g.drawString("JEDI", x, y, Graphics.TOP | Graphics.HCENTER);
// mais operações com desenhos...

2.3. Evitar o modificador sincronizado

Usar um modificador do tipo sincronizado aumentará a velocidade de execução de sua classe, pois
ao mesmo tempo que ele terá de executar algumas medidas extras e não poderá ser acessado
simultaneamente.

2.4. Passar o menor número de parâmetros possível

Ao chamar um método, o interpretador empurra todos os parâmetros para a pilha da execução.


Passar muitos parâmetros afeta a velocidade de execução e utiliza muita memória.

2.5. Reduzir as chamadas de métodos

As chamadas de métodos ocupam muita memória e tempo de execução. Veja o item anterior.

2.6. Atrasar as inicializações

Para ganhar tempo no início das aplicações, atrase todas as inicializações pesadas até que sejam

Desenvolvimento de Aplicações Móveis 5


JEDITM

necessárias. Não ponha inicializações no construtor de MIDlet's ou no método startApp. Apressar


a inicialização fará com que a aplicação demore mais para ficar plenamente utilizável. A maioria
dos usuários recusaria aplicações que exigem um longo tempo para inicializar. Lembre-se que o
tempo de carga da aplicação afeta diretamente a primeira impressão que o usuário tem de seu
programa.

2.7. Utilizar arrays (matrizes) ao invés de Collection

Acessar matrizes é mais rápido do que usar um objeto do tipo Vector.

2.8. Utilizar atributos locais

É mais rápido acessar variáveis locais do que variáveis globais.

Desenvolvimento de Aplicações Móveis 6


JEDITM

3. Tamanho do Arquivo JAR


3.1. Utilizar um ofuscador (obfuscator)

A intenção original do ofuscador é complicar o máximo possível os arquivos da classe compilada


para que seja muito complicado reverter essa situação. O Netbeans e o pacote Mobility vêm com
um ofuscador. Não está ativo por padrão. Selecione a aba propriedade da aplicação e clique no
ícone "Obfuscating":

Figura 1: Ativando o ofuscador

São dez níveis de ofuscação e deve-se ser o mais agressivo possível em se tratando de
ofuscamento:

Figura 2: Maximizando o ofuscador

Desenvolvimento de Aplicações Móveis 7


JEDITM

Entretanto, o processo de ofuscar também reduz o tamanho da aplicação. Um dos métodos


empregados pelo ofuscador é renomear as classes utilizando letras simples (por exemplo, classe
A). O ofuscador consegue fazer isto por possuir um transformador de métodos. Se o método tiver
um modificador private ou protected, então podemos, com segurança, supor que este método não
será usado por outros pacotes e poderá, conseqüentemente, ser renomeado sem problemas.

3.2. Compressão dos arquivos JAR

Antes de distribuir seu aplicativo, deve-se comprimir o arquivo JAR final para distribuí-lo. Um
arquivo tipo JAR é um arquivo tipo ZIP, e um arquivo tipo ZIP possui diversos níveis de
compressão (incluindo a não compressão). O NetBeans não implementa os níveis de compressão.

Figura 3: Comprimindo o arquivo JAR

Para definir a compressão do JAR, abra a janela de propriedades do projeto e selecione a opção
“Creating JAR”. Marque a opção “Compress JAR” para comprimir o arquivo final. Não esqueça de
gerar (rebuild) o projeto novamente.
O Netbeans armazena o arquivo JAR final na pasta denominada dist abaixo da pasta de projeto.
Pode-se renomear a extensão do arquivo JAR para um arquivo com extensão ZIP e abrí-lo com
qualquer programa de compressão (exemplo, WinZip) para ver os tamanhos de seus arquivos de
classe compilados.

3.3. Evitar a criação de classes desnecessárias

Isto pode parecer contraditório aos princípios da orientação a objetos, entretanto uma classe
vazia e simples como:
public class EmptyClass {
public EmptyClass(){}
}

será compilada em um arquivo do tipo class com com um tamanho de arquivo de no mínimo
250kb (não comprimíveis). Compile esta classe vazia e observe o resultado.

Desenvolvimento de Aplicações Móveis 8


JEDITM

3.4. Evitar a criação de interfaces

Esta técnica está relacionada com a anteriormente vista. Quanto mais classes e interfaces, mais
(kilo)bytes teremos na aplicação final.

3.5. Evitar a criação de classes internas e anônimas

Classes internas (inner class) são classificadas do mesmo modo. Classes anônimas (anonymous
class) podem não ter um nome, entretanto ocupam o mesmo espaço nas definições de classe.

3.6. Utilizar um objeto único (padrão Singleton) para múltiplos objetos

Isto reduz o número de classes em sua aplicação. Faça com que seu MIDlet implemente a
interface CommandListener e lhe ajudaria a reduzir seu pacote através de uma classe (isso é 250,
ou mais, bytes menos).

3.7. Utilizar um pacote "padrão" (não significa não usar package)

Utilize um tamanho de pacote pequeno, encurtando (e não usando) nomes de pacote, o que
contribui para a redução dos bytes.

3.8. Utilizar o limite dos inicializadores estáticos

Usando inicializações estáticas, tipo:


int[] tones = { 64, 63, 65, 76, 45, 56, 44, 88 };

seria traduzido pelo compilador de Java nas seguintes declarações:


tones[0] = 64;
tones[1] = 63;
tones[2] = 65;
tones[3] = 76;
tones[4] = 45;
tones[5] = 56;
tones[6] = 44;
tones[7] = 88;

Este exemplo ilustra apenas oito elementos. Pense na possibilidade de inicializar centenas de
valores que utilizam declarações separadas. Tentar realizar isso, estaria muito acima do tamanho
das possíveis aplicações.
Como uma alternativa, é possível utilizar o método getResourceAsStream() para obter valores de
um arquivo ou utilizar uma única String para armazenar os valores do array.

3.9. Combinar as imagens em um único arquivo

Imagens são comprimidas melhor quando estão agrupadas em um único arquivo de imagem. Isso
é porque a compressão do formato de imagem (formato PNG) é mais específico para imagens do
que o método de compressão do arquivo JAR. Há técnicas para se obter uma imagem específica
de uma imagem maior, tal como recortá-la.

3.10. Experimentar métodos diferentes de compressão de imagens

Métodos de compressão não são criados de forma semelhante. Alguns podem comprimir melhor
alguns tipos de imagem mas podem ter relação de compressão pobre em outros tipos. Escolha
um formato de imagem que melhora a relação de compressão. Às vezes, a relação de compressão
também é afetada pelo software de imagem que se está utilizando. Experimente com
manipulação de imagem diferentes programas para conseguir tamanhos de imagem melhores.

Desenvolvimento de Aplicações Móveis 9


JEDITM

3.11. Utilizar classes pré-instaladas

Não reinvente a roda. Utilize classes disponíveis na plataforma que está usando. Criar suas
classes não só aumenta o tamanho da aplicação como também diminui a estabilidade.

Desenvolvimento de Aplicações Móveis 10


JEDITM

4. Rede
4.1. Utilizar threads

Utilize uma thread separada para sua função de rede para evitar travamentos da tela.

4.2. Comprimir os dados da rede

Utilize dados comprimidos para diminuir o tráfego de rede da sua aplicação. Isso requerer que seu
cliente e servidor estejam utilizando o mesmo protocolo de rede e método de compressão.
Comprimir XML resulta em melhor taxa de compressão porque o XML é representado em formato
texto.

4.3. Reduzir o tráfego de rede

Já que as comunicações via rede são lentas e onerosas, tente o quanto mais possível colocar
dentro de uma única requisição de rede vários comandos. Isso reduzirá a sobrecarga imposta
pelos protocolos de rede.

Desenvolvimento de Aplicações Móveis 11


JEDITM

5. Uso de Memória
5.1. Utilizar estruturas de dados mais compactas

Utilize estruturas de dados mais amigáveis para memória. Arrays espaçados podem ser
representados de outra maneira sem consumir a mesma quantidade de memória.
Existe um equilíbrio quando se otimizando tamanho e velocidade. Utilizar estruturas complexas
de dados pode afetar a velocidade de execução do programa.

5.2. Liberar objetos não usados para o Garbage Collector

Libere objetos que não serão mais utilizados para o Garbage Collector – tela, conexões de rede,
Registros RMS, entre outros. Ao atribuir para estes objetos o valor nulo, informamos ao Garbage
Collector que estes objetos podem ser seguramente descarregados da memória.

5.3. Criar as telas que são raramente usadas como objetos anônimos

Criar os objetos de Tela que são raramente utilizadas (como telas de “auxílio” e “sobre o
sistema”) como objetos anônimos libera a necessidade de memória heap, embora tenhamos que
pagar o preço por uma carga mais lenta destas telas em particular. A memória heap destas telas
supostamente seria ocupada enquanto elas não estivessem sendo usadas e pode ajudar na
conservação de memória.
public void commandAction(Command c, Displayable d) {
if (c == helpCommand) {
display.setCurrent(new HelpForm());
}
}

Desenvolvimento de Aplicações Móveis 12


JEDITM

6. Exercícios
6.1. Outras idéias de otimização

Discuta outras idéias de otimização que possui ou tem em mente ou, ainda, outras técnicas que
desenvolveu. Compartilhe-as.

Desenvolvimento de Aplicações Móveis 13


Módulo 5
Desenvolvimento de Aplicações Móveis

Lição 9
Pacotes Opcionais

Versão 1.0 - Set/2007


JEDITM

1. Objetivos
Nesta lição, iremos aprofundar em como escrever, construir, utilizar o emulador e o empacotador
de aplicações J2ME. O ambiente de programação que iremos utilizar será o Netbeans.
Nem todos os dispositivos são criados de maneira semelhante, pois cada um deles possui
características diferentes. Por isso pode ser muito complicado para se criar uma especificação
padrão que atenda a todos os dispositivos.
Para acomodar as diferentes capacidades de cada dispositivo, a tecnologia MIDP definiu vários
pacotes opcionais. Esses pacotes são específicos para atender dispositivos específicos que tenham
esses recursos. Iremos aprender como utilizar a Mobile Media API (MMAPI) e a Wireless
Messaging API (WMA).
Ao final desta lição, o estudante será capaz de:
• Saber quais são as funcionalidade oferecidas pela Mobile Media API
• Reproduzir tons simples
• Reproduzir um arquivo de áudio de uma rede e de um JAR
• Enviar e receber mensagens SMS

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. Mobile Media API (MMAPI)


A Mobile Media API (MMAPI) permite-nos gerar tons, tocar e gravar áudio e vídeo nos dispositivos
compatíveis. A reprodução e a gravação de mídia são tratadas por dois objetos: o DataSource e o
Player.

DataSource Player

Figura 1: Relação entre DataSource e Player

O DataSource trata de detalhes de como obter o dado de uma fonte. A fonte pode ser um arquivo
de um JAR ou de uma rede (via HTTP), um registro de um RMS, uma conexão de streaming de
um servidor ou outra fonte proprietária. O Player não tem o que se preocupar sobre de onde o
dado vem ou de que uma maneira ele pode ser obtido. Tudo o que o Player necessita fazer é ler
um dado de um DataSource, processar e exibir ou reproduzir a mídia para o dispositivo de saída.
O terceiro ator na nossa cena é o Manager. O Manager cria players de DataSources. O Manager
tem métodos para criar Players vindos dos locais de mídia (através de URL), DataSources e
InputStreams.

Manager

DataSource Player

Figura 2: Relacionamento do Manager com o DataSource e Player

Pode-se consultar propriedades MMAPI via String System.getProperty(String key).

Chave Descrição
microedition.media.version A versão da especificação MMAPI implementada pelo dispositivo.
Exemplo: "1.1"
supports.mixing Retorna "true" se o dispositivo suporta mixagem de áudio. Pode
reproduzir os dois últimos tons simultaneamente. Pode ter dois
Players reproduzindo áudio simultaneamente e pode reproduzir
um tom enquanto o outro Player está reproduzindo áudio ao
mesmo tempo.
supports.audio.capture Retorna "true" se a captura de áudio é suportada.

supports.video.capture Retorna "true" se a captura de vídeo é suportada.

supports.recording Retorna "true" se a gravação de áudio é suportada.

2.1. Geração de Tons

Para reproduzir tons é necessário chamar o método estático Manager.playTone(int tom, int
duration, int volume). Os valores válidos para o parâmetro tom vão de 0 a 127. O parâmetro
duration representa a duração da reprodução do tom e deve ser especificada em milissegundos. O
parâmetro volume varia de 0 a 100.

Desenvolvimento de Aplicações Móveis 5


JEDITM

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
import java.io.*;

public class ToneMIDlet extends MIDlet implements CommandListener{


private Command exitCommand, playCommand;
private Form form;
private Gauge volumeGauge;
private Gauge durationGauge;
private Gauge toneGauge;
private Display display;
private int duration = 2; // seconds
private int volume = 100;
private int tone = ToneControl.C4;
private static int MAX_VOLUME = 100;
private static int MAX_TONE = 127;
private static int MAX_DURATION = 5;

public ToneMIDlet() {
playCommand = new Command("Play", Command.OK, 1);
exitCommand = new Command("Exit", Command.EXIT, 1);
volumeGauge = new Gauge("Volume", true, MAX_VOLUME, volume);
toneGauge = new Gauge("Tone", true, MAX_TONE, tone);
durationGauge = new Gauge("Duration",true,MAX_DURATION,duration);

form = new Form("Tone Player");


form.addCommand(playCommand);
form.addCommand(exitCommand);
form.append(volumeGauge);
form.append(durationGauge);
form.append(toneGauge);
}
public void startApp() {
display = Display.getDisplay(this);
form.setCommandListener(this);
display.setCurrent(form);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void commandAction(Command c, Displayable d) {
if (c == exitCommand) {
notifyDestroyed();
}
if (c == playCommand){
try {
volume = volumeGauge.getValue();
tone = toneGauge.getValue();
duration = durationGauge.getValue();
Manager.playTone(tone, duration*1000, volume);
} catch (MediaException mex){}
}
}
}

2.2. Tocando Áudio

Por conveniência, o método Manager.createPlayer(String URI) cria um objeto player a partir de


uma URI.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
import java.io.*;

Desenvolvimento de Aplicações Móveis 6


JEDITM

public class NetAudioMidlet extends MIDlet implements CommandListener{


private Command exitCommand, playCommand;
private Form form;
private Gauge volumeGauge;
private Display display;
private int volume = 100;
private static int MAX_VOLUME = 100;
Player player;

public NetAudioMidlet() {
playCommand = new Command("Play", Command.OK, 1);
exitCommand = new Command("Exit", Command.EXIT, 1);
volumeGauge = new Gauge("Volume", true, MAX_VOLUME, volume);
form = new Form("Audio Player");
form.addCommand(playCommand);
form.addCommand(exitCommand);
form.append(volumeGauge);
}
public void startApp() {
display = Display.getDisplay(this);
form.setCommandListener(this);
display.setCurrent(form);
try {
player = Manager.createPlayer("http://localhost:8084/Chapter07/bong.wav");
player.realize();
player.prefetch();
} catch (IOException ioex) {
display.setCurrent(new Alert("IO Exception",
ioex.getMessage(),
null, AlertType.ERROR));
} catch (MediaException mex) {
display.setCurrent(new Alert("Media Exception",
mex.getMessage(),
null, AlertType.ERROR));
}
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void commandAction(Command c, Displayable d) {
if (c == exitCommand) {
notifyDestroyed();
}
if (c == playCommand){
try {
VolumeControl control = (VolumeControl)
player.getControl("VolumeControl");
if (control != null){
control.setLevel(volumeGauge.getValue());
}

player.start();
} catch (MediaException mex) {
display.setCurrent(new Alert("Media Exception",
mex.getMessage(), null, AlertType.ERROR));
} catch (Exception ex){
display.setCurrent(new Alert("Exception",
ex.getMessage(), null, AlertType.ERROR));
}
}
}
}

Também é possível tocar uma mídia a partir de um arquivo inserido no JAR do projeto.
Entretanto, deve ser criado um objeto do tipo Stream que será repassado para o método
Manager.createPlayer().

Desenvolvimento de Aplicações Móveis 7


JEDITM

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
import java.io.*;

public class AudioMidlet extends MIDlet implements CommandListener{


private Command exitCommand, playCommand;
private Form form;
private Gauge volumeGauge;
private Display display;
private int volume = 100;
private static int MAX_VOLUME = 100;
Player player;

public AudioMidlet() {
playCommand = new Command("Play", Command.OK, 1);
exitCommand = new Command("Exit", Command.EXIT, 1);
volumeGauge = new Gauge("Volume", true, MAX_VOLUME, volume);
form = new Form("Audio Player");
form.addCommand(playCommand);
form.addCommand(exitCommand);
form.append(volumeGauge);
}
public void startApp() {
display = Display.getDisplay(this);
form.setCommandListener(this);
display.setCurrent(form);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void commandAction(Command c, Displayable d) {
if (c == exitCommand) {
notifyDestroyed();
}
if (c == playCommand){
try {
InputStream stream = getClass().
getResourceAsStream("bong.wav");
player = Manager.createPlayer(stream, "audio/x-wav");
player.realize();

VolumeControl control = (VolumeControl)


player.getControl("VolumeControl");
if (control != null){
control.setLevel(volumeGauge.getValue());
}

player.start();
} catch (MediaException mex) {
display.setCurrent(new Alert("Media Exception",
mex.getMessage(), null, AlertType.ERROR));

} catch (Exception ex){


display.setCurrent(new Alert("Exception",
ex.getMessage(), null, AlertType.ERROR));
}
}
}
}

Desenvolvimento de Aplicações Móveis 8


JEDITM

3. Wireless Messaging API (WMA)


Utilizar o Wireless Messaging API é quase semelhante à maneira que uma conexão é feita através
de soquetes e Datagramas. Utiliza-se o mesmo aplicativo – Generic Connection Framework (GCF).
A URL de conexão possui o seguinte formato "sms://+639178888888", onde "+639178888888" é
o número do telefone para qual se deseja enviar a mensagem.
public void sendSMS(String number, String message) throws Exception{
String url = "sms://" + number;
MessageConnection connection = (MessageConnection) Connector.open(url);
TextMessage msg = (TextMessage) connection.newMessage(
MessageConnection.TEXT_MESSAGE);
msg.setPayloadText(message);
connection.send(msg);
connection.close();
}

3.1. Enviando uma mensagem SMS

O desenvolvimento de aplicações móveis com o Netbeans é muito simples. Não é necessário


enviar mensagens reais de SMS apenas para testar a aplicação que estamos desenvolvendo. O
Netbeans (com o pacote Mobility) possui a ferramenta J2ME Wireless Toolkit. Esta ferramenta vem
com um emulador e inclui também aplicativos para testar o envio e recebimento de mensagens
do tipo SMS. É possível configurar o número do telefone utilizando as preferências do WMA
acessando a partir do menu principal:
• Tools
• Java Platform Manager
• J2ME Wireless Toolkit 2.2
• Tools & Extensions
• Preferences -> WMA
• Utilities -> WMA: Open Console

Figura 3: Java Platform Manager

Desenvolvimento de Aplicações Móveis 9


JEDITM

Figura 4: Java Platform Manager - Devices

Figura 5: Java Platform Manager – Tools & Extensions

Desenvolvimento de Aplicações Móveis 10


JEDITM

Figura 6: J2ME Wireless Toolkit – Preferences

Figura 7: J2ME Wireless Toolkit – Utilities e Console

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import javax.wireless.messaging.*;

public class SMSMidlet extends MIDlet implements CommandListener, Runnable {

private Command exitCommand, sendCommand;

Desenvolvimento de Aplicações Móveis 11


JEDITM

private Form form;


private TextField addressField, mesgField;
private Display display;

public SMSMidlet() {
sendCommand = new Command("Send", Command.OK, 1);
exitCommand = new Command("Exit", Command.EXIT, 1);

addressField = new TextField(


"Phone Number", "+5550000", 32, TextField.ANY);
mesgField = new TextField(
"Message", "hello, world!", 160, TextField.ANY);

form = new Form("SMS Message");


form.append(addressField);
form.append(mesgField);
form.addCommand(sendCommand);
form.addCommand(exitCommand);
}

public void startApp() {


display = Display.getDisplay(this);
form.setCommandListener(this);
display.setCurrent(form);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void commandAction(Command c, Displayable d) {
if (c == exitCommand) {
notifyDestroyed();
}
if (c == sendCommand) {
Thread thread = new Thread( this );
thread.start();
}
}
public void sendSMS(String number, String message) throws Exception{
String url = "sms://" + number;
MessageConnection connection =
(MessageConnection) Connector.open(url);
TextMessage msg = (TextMessage) connection.newMessage(
MessageConnection.TEXT_MESSAGE);
msg.setPayloadText(message);
connection.send(msg);
connection.close();
}
public void run() {
try {
String address = addressField.getString();
String message = mesgField.getString();
sendSMS(address, message);
display.setCurrent(new Alert("SMS Message", "Message Sent\n"
+ "To: " + address + "\n" + "Message: " + message, null,
AlertType.INFO));
} catch (Exception ex) {
display.setCurrent(new Alert("SMS Error", ex.getMessage(),
null, AlertType.ERROR));
}
}
}

Desenvolvimento de Aplicações Móveis 12


JEDITM

Figura 8: Execução da aplicação

3.2. Recebendo mensagens SMS

Para receber um mensagem de texto, abra uma MessageConnection especificando uma porta. O
Protocolo para mensagem SMS é "sms". Este comando ficará esperando até receber uma
mensagem de SMS pela porta 8888:
conn = (MessageConnection) Connector.open("sms://:8888");

Devemos registrar nossa aplicação para ser um receptor da mensagem de forma que o AMS
notifique o MIDlet da chegada da mensagem.
conn.setMessageListener(this);

O método notifyIncomingMessage será chamado pelo AMS quando uma mensagem for recebida

Desenvolvimento de Aplicações Móveis 13


JEDITM

pelo dispositivo. Precisamos criar uma Thread separada para as mensagens de leitura de forma
que o método que for chamado novamente possa ter uma saída imediata.
public void notifyIncomingMessage(MessageConnection messageConnection) {
if (thread == null){
thread = new Thread(this);
thread.start();
}
}

E, deste modo, será utilizado o método run(), do qual obteremos a mensagem:


public void run(){
try {
Message mesg = conn.receive();
if (mesg != null && mesg instanceof TextMessage) {
TextMessage text = (TextMessage) mesg;
addressField.setText(text.getAddress());
mesgField.setText(text.getPayloadText());
dateField.setText("" + text.getTimestamp());
statusField.setText("Message received.");
}
} catch (Exception e) {
statusField.setText("Error: " + e.getMessage());
}
thread = null;
}

Este é o código completo para receber e listar a mensagem SMS:


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import javax.wireless.messaging.*;

public class SMSReceiverMidlet extends MIDlet


implements CommandListener, MessageListener, Runnable {
private Command exitCommand, sendCommand;
private Form form;
private StringItem statusField, addressField, mesgField, dateField;
private Display display;
private MessageConnection conn;
private Thread thread;
private String port = "8888";

public SMSReceiverMidlet() {
exitCommand = new Command("Exit", Command.EXIT, 1);
statusField = new StringItem("Status:", "");
addressField = new StringItem("From:", "");
mesgField = new StringItem("Message:", "");
dateField = new StringItem("Timestamp:", "");
form = new Form("SMS Receiver");
form.append(statusField);
form.append(addressField);
form.append(mesgField);
form.append(dateField);
form.addCommand(exitCommand);
}
public void startApp() {
display = Display.getDisplay(this);
form.setCommandListener(this);

startReceiver();
display.setCurrent(form);
}
public void pauseApp() {
thread = null;

Desenvolvimento de Aplicações Móveis 14


JEDITM

}
public void destroyApp(boolean unconditional) {
thread = null;
if (conn != null){
try {
conn.close();
} catch (Exception ex){}
}
}
public void commandAction(Command c, Displayable d) {
if (c == exitCommand) {
notifyDestroyed();
}
}
private void startReceiver(){
try {
String addr = "sms://:" + port;
if (conn == null){
conn = (MessageConnection) Connector.open(addr);
conn.setMessageListener(this);
statusField.setText(
"waiting for message at port " + port);
}
} catch (Exception ex){
statusField.setText("Cannot open connection on port "
+ port + ":" + ex.getMessage());
}
thread = new Thread(this);
thread.start();
}
public void notifyIncomingMessage(MessageConnection messageConn) {
if (thread == null){
thread = new Thread(this);
thread.start();
}
}
public void run(){
try {
Message mesg = conn.receive();
if (mesg != null && mesg instanceof TextMessage) {
TextMessage text = (TextMessage) mesg;
addressField.setText(text.getAddress());
mesgField.setText(text.getPayloadText());
dateField.setText("" + text.getTimestamp());
statusField.setText("Message received.");
} else {
statusField.setText(
"Non-text message received: "
+ mesg.getClass().toString());
}
} catch (Exception e) {
statusField.setText("Error: " + e.getMessage());
}
thread = null;
}
}

Desenvolvimento de Aplicações Móveis 15


JEDITM

Figura 9: Recebimento da mensagem

Desenvolvimento de Aplicações Móveis 16


JEDITM

4. Exercícios
4.1. Tocador de áudio

Crie uma MIDlet que toque um arquivo de áudio por um número indefinido de vezes. O arquivo de
áudio deve ser lido a partir do JAR da aplicação. Dica: envie uma propriedade para o objeto Player
controlar o laço.

4.2. Auto-respondedor de SMS

Crie uma MIDlet que responda automaticamente quando receber uma mensagem de texto. Dica:
modifique a classe SMSReceiverMidlet e utilize a mesma conexão para enviar a mensagem de
resposta.

Desenvolvimento de Aplicações Móveis 17


Módulo 5
Desenvolvimento de Aplicações Móveis

Lição 10
Outros Tópicos

Versão 1.0 - Set/2007


JEDITM

1. Objetivos
Timers e TimeTasks permitem programar tarefas para que sejam executadas em um horário
determinado. A tarefa pode ainda ser programada para que se repita num intervalo de tempo
designado pelo programador.
Ao final desta lição, o estudante será capaz de:
• Programar tarefas utilizando Timers (marcadores)
• Registrar o recebimento de conexões no Registro

Desenvolvimento de Aplicações Móveis 4


JEDITM

2. Timers
É possível criar uma tarefa utilizando-se a herança, estendendo a classe TimerTask e
implementando o método run(). Este método será executado baseado na programação do Timer.
class CounterTask extends TimerTask {
int counter = 0;
public void run() {
System.out.println("Counter: " + counter++);
}
}

Para programar uma tarefa, cria-se um objeto do tipo Timer e utiliza-se o método schedule() do
Timer para que se possa configurar o andamento da tarefa. Cada Timer roda em uma thread
independente. O método schedule() possui várias formas diferentes. É possível especificar o
tempo de início para a tarefa utilizando um tempo em milissegundos ou especificando uma data
absoluta (java.util.Date). O terceiro parâmetro para o método é o período de repetição da tarefa.
Se o período de repetição for especificado, a tarefa será executada a cada "período" de
milissegundos definido.
Timer timer = new Timer();
TimerTask task = new CounterTask();
// inicia a tarefa em 8 segundos, e repete a cada segundo
timer.schedule(task, 8000, 1000);

Para parar a execução do objeto Timer, utiliza-se o método close(). Isto fará com que a Thread
contendo o Timer seja interrompida e a tarefa agendada, descartada. Lembre-se sempre de que
uma vez que o Timer tenha sido parado, não poderá ser reiniciado.
void schedule( Agendar a tarefa para que seja executada a cada período (em
TimerTask task, milissegundos).
long delay)
void schedule( Agendar a tarefa para que ela seja repetida, começando após
TimerTask task, o período especificado (em milissegundos).
long delay,
long period)
void schedule( Agenda a tarefa para que seja executada num tempo
TimerTask task, específico.
Date time)
void schedule( Agenda a tarefa para que ela seja repetida, começando pelo
TimerTask task, tempo especificado.
Date time,
long period)
void cancel() Interrompe o timer e descarta qualquer tarefa agendada.

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.io.*;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Date;

public class TimerMidlet extends MIDlet implements CommandListener{


private Command exitCommand;
private Form form;
private StringItem textField;
private Display display;
private Timer timer;

public TimerMidlet() {
exitCommand = new Command("Exit", Command.EXIT, 1);
textField = new StringItem("Counter", "");
timer = new Timer();

Desenvolvimento de Aplicações Móveis 5


JEDITM

TimerTask task = new CounterTask(this);


timer.schedule(task, 2000, 1000);
form = new Form("Timer Test");
form.addCommand(exitCommand);
form.append(textField);
}
public void startApp() {
display = Display.getDisplay(this);
form.setCommandListener(this);
display.setCurrent(form);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {
timer.cancel();
}
public void commandAction(Command c, Displayable d) {
if (c == exitCommand) {
destroyApp(true);
notifyDestroyed();
}
}
public void setText(String text){
textField.setText(text);
}
}
class CounterTask extends TimerTask {
int counter = 0;
TimerMidlet midlet;

public CounterTask(TimerMidlet midlet){


this.midlet = midlet;
}
public void run() {
counter++;
midlet.setText("" + counter);
System.out.println("Counter: " + counter);
}
}

Desenvolvimento de Aplicações Móveis 6


JEDITM

3. Registro de Conexões
Permite que os MIDlets registrem as conexões com o software de gerenciamento da aplicação
(AMS). Se o programa não estiver em execução, o AMS ficará esperando por conexões nos limites
dos endereços registrados pelas aplicações. Quase todos os tipos de conexões são suportadas,
incluindo SMS, CBS e AMS.
É possível fazer o registro de novas conexões de duas maneiras: da maneira estática, utilizando o
arquivo application descriptor (JAD), ou dinamicamente durante o tempo de execução, utilizando
a API PushRegistry.
Iremos demostrar como utilizar a API PushRegistry. Pressionar o botão direito do mouse sobre o
Project Name e selecionar properties para abrir a página de propriedades do projeto. Selecionar a
opção de Push Registry.

Figura 1: Janela de Propriedades do Projeto

Pressionar o botão add... para registrar uma nova conexão:

Figura 2: Adicionando um novo registro

Class Name: SMSReceiveMidlet


Sender IP: *

Desenvolvimento de Aplicações Móveis 7


JEDITM

Connection String: sms://:50000

Verifique a conexão adicionada na figura abaixo.

Figura 3: Registro da Conexão

Selecionar a opção API Permissions e pressionar o botão Add para inserir uma permissão do
MIDlet para uma biblioteca em particular.

Figura 4: Adicionando permissões

Adicionar as seguintes bibliotecas:


javax.microedition.io.PushRegistry
javax.microedition.io.Connector.sms
javax.wireless.messaging.sms.receive
javax.wireless.messaging.sms.send

Desenvolvimento de Aplicações Móveis 8


JEDITM

Figura 5: Permissões adicionadas

Para finalizar, criar um atributo para configurar automaticamente a porta, selecionar Attributes
conforme a seguinte janela:

Figura 6: Adicionar novos atributos

Pressionar o botão Add... para adicionar um novo atributo do tipo Custom.

Desenvolvimento de Aplicações Móveis 9


JEDITM

Figura 7: Adicionando o atributo

Encerre a janela de configuração do projeto pressionando o botão OK e execute a suite do MIDlet.


Executar a aplicação duas vezes para conseguir dois emuladores conforme as figuras:

Figura 8: Emulador do aplicativo

Observe que o primeiro telefone possui o número +5550000 e o segundo +5550001. Selecionar
a opção SMSReceiveMidlet no primeiro telefone. Uma mensagem solicitando a autorização para
comunicação via mensagem será mostrada, selecionar a opção Yes e deixar em modo de espera:

Figura 9: Modo de Espera da Mensagem

Desenvolvimento de Aplicações Móveis 10


JEDITM

No segundo telefone, selecionar SMSSendMidlet, informar o endereço 5550000 e pressionar OK.

Figura 10: Emulador do aplicativo

Digitar uma mensagem e pressionar Send:

Figura 11: Janela de confirmação

Responder afirmativamente à mensagem de confirmação da comunicação:

Figura 12: Confirmação da Comunicação

Responder afirmativamente a próxima mensagem e aguardar o envio. Observar no primeiro


telefone o recebimento da mensagem.

Figura 13: Aplicativo concluído

Desenvolvimento de Aplicações Móveis 11


JEDITM

3.1. Programas

Classe Auxiliar
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.wireless.messaging.*;
import java.io.IOException;

public class SMSSender implements CommandListener, Runnable {


Command sendCommand = new Command("Send", Command.OK, 1);
Command backCommand = new Command("Back", Command.BACK, 2);
Display display;
String smsPort;
String destinationAddress;
TextBox messageBox;
Displayable backScreen;
Displayable sendingScreen;

public SMSSender(String smsPort, Display display,


Displayable backScreen, Displayable sendingScreen) {
this.smsPort = smsPort;
this.display = display;
this.destinationAddress = null;
this.backScreen = backScreen;
this.sendingScreen = sendingScreen;
messageBox = new TextBox("Enter Message", null, 65535, TextField.ANY);
messageBox.addCommand(backCommand);
messageBox.addCommand(sendCommand);
messageBox.setCommandListener(this);
}
public void promptAndSend(String destinationAddress) {
this.destinationAddress = destinationAddress;
display.setCurrent(messageBox);
}
public void commandAction(Command c, Displayable s) {
try {
if (c == backCommand) {
display.setCurrent(backScreen);
} else if (c == sendCommand) {
display.setCurrent(sendingScreen);
new Thread(this).start();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void run() {
String address = destinationAddress + ":" + smsPort;
MessageConnection smsconn = null;
try {
smsconn = (MessageConnection)Connector.open(address);
TextMessage txtmessage = (TextMessage)smsconn.newMessage(
MessageConnection.TEXT_MESSAGE);
txtmessage.setAddress(address);
txtmessage.setPayloadText(messageBox.getString());
smsconn.send(txtmessage);
} catch (Throwable t) {
System.out.println("Send caught: ");
t.printStackTrace();
}
if (smsconn != null) {
try {
smsconn.close();
} catch (IOException ioe) {
System.out.println("Closing connection caught: ");

Desenvolvimento de Aplicações Móveis 12


JEDITM

ioe.printStackTrace();
}
}
}
}

Midlet para receber mensagem SMS


import javax.microedition.midlet.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.wireless.messaging.*;
import java.io.IOException;

public class SMSReceiveMidlet extends MIDlet


implements CommandListener, Runnable, MessageListener {
Command exitCommand = new Command("Exit", Command.EXIT, 2);
Command replyCommand = new Command("Reply", Command.OK, 1);
Alert content;
Display display;
Thread thread;
String[] connections;
boolean done;
String smsPort;
MessageConnection smsconn;
Message msg;
String senderAddress;
Alert sendingMessageAlert;
SMSSender sender;
Displayable resumeScreen;

public SMSReceiveMidlet() {
smsPort = getAppProperty("SMS-Port");
display = Display.getDisplay(this);
content = new Alert("SMS Receive");
content.setTimeout(Alert.FOREVER);
content.addCommand(exitCommand);
content.setCommandListener(this);
content.setString("Receiving...");
sendingMessageAlert = new Alert("SMS", null, null, AlertType.INFO);
sendingMessageAlert.setTimeout(5000);
sendingMessageAlert.setCommandListener(this);
sender = new SMSSender(smsPort, display, content, sendingMessageAlert);
resumeScreen = content;
}
public void startApp() {
String smsConnection = "sms://:" + smsPort;
if (smsconn == null) {
try {
smsconn = (MessageConnection) Connector.open(smsConnection);
smsconn.setMessageListener(this);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
connections = PushRegistry.listConnections(true);
if (connections == null || connections.length == 0) {
content.setString("Waiting for SMS on port " + smsPort + "...");
}
done = false;
thread = new Thread(this);
thread.start();

display.setCurrent(resumeScreen);
}
public void notifyIncomingMessage(MessageConnection conn) {
if (thread == null) {

Desenvolvimento de Aplicações Móveis 13


JEDITM

done = false;
thread = new Thread(this);
thread.start();
}
}
public void run() {
try {
msg = smsconn.receive();
if (msg != null) {
senderAddress = msg.getAddress();
content.setTitle("From: " + senderAddress);
if (msg instanceof TextMessage) {
content.setString(((TextMessage)msg).getPayloadText());
} else {
StringBuffer buf = new StringBuffer();
byte[] data = ((BinaryMessage)msg).getPayloadData();
for (int i = 0; i < data.length; i++) {
int intData = (int)data[i] & 0xFF;
if (intData < 0x10) {
buf.append("0");
}
buf.append(Integer.toHexString(intData));
buf.append(' ');
}
content.setString(buf.toString());
}
content.addCommand(replyCommand);
display.setCurrent(content);
}
} catch (IOException e) {
// e.printStackTrace();
}
}
public void pauseApp() {
done = true;
thread = null;
resumeScreen = display.getCurrent();
}
public void destroyApp(boolean unconditional) {
done = true;
thread = null;
if (smsconn != null) {
try {
smsconn.close();
} catch (IOException e) {
// Ignora erros no caso de finalização
}
}
}
public void commandAction(Command c, Displayable s) {
try {
if (c == exitCommand || c == Alert.DISMISS_COMMAND) {
destroyApp(false);
notifyDestroyed();
} else if (c == replyCommand) {
reply();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void reply() {
String address = senderAddress.substring(6);
String statusMessage = "Sending message to " + address + "...";
sendingMessageAlert.setString(statusMessage);
sender.promptAndSend(senderAddress);
}

Desenvolvimento de Aplicações Móveis 14


JEDITM

Midlet para enviar mensagem SMS


import javax.microedition.midlet.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.wireless.messaging.*;
import java.io.IOException;

public class SMSSendMidlet extends MIDlet implements CommandListener {


Command exitCommand = new Command("Exit", Command.EXIT, 2);
Command okCommand = new Command("OK", Command.OK, 1);
Display display;
String smsPort;
TextBox destinationAddressBox;
Alert errorMessageAlert;
Alert sendingMessageAlert;
SMSSender sender;
Displayable resumeScreen = null;

public SMSSendMidlet() {
smsPort = getAppProperty("SMS-Port");
display = Display.getDisplay(this);
destinationAddressBox = new TextBox("Destination Address?",
null, 256, TextField.PHONENUMBER);
destinationAddressBox.addCommand(exitCommand);
destinationAddressBox.addCommand(okCommand);
destinationAddressBox.setCommandListener(this);
errorMessageAlert = new Alert("SMS", null, null, AlertType.ERROR);
errorMessageAlert.setTimeout(5000);
sendingMessageAlert = new Alert("SMS", null, null, AlertType.INFO);
sendingMessageAlert.setTimeout(5000);
sendingMessageAlert.setCommandListener(this);
sender = new SMSSender(smsPort, display, destinationAddressBox,
sendingMessageAlert);
resumeScreen = destinationAddressBox;
}
public void startApp() {
display.setCurrent(resumeScreen);
}
public void pauseApp() {
resumeScreen = display.getCurrent();
}
public void destroyApp(boolean unconditional) {
}
public void commandAction(Command c, Displayable s) {
try {
if (c == exitCommand || c == Alert.DISMISS_COMMAND) {
destroyApp(false);
notifyDestroyed();
} else if (c == okCommand) {
promptAndSend();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void promptAndSend() {
String address = destinationAddressBox.getString();
if (!SMSSendMidlet.isValidPhoneNumber(address)) {
errorMessageAlert.setString("Invalid phone number");
display.setCurrent(errorMessageAlert, destinationAddressBox);
return;
}
String statusMessage = "Sending message to " + address + "...";
sendingMessageAlert.setString(statusMessage);

Desenvolvimento de Aplicações Móveis 15


JEDITM

sender.promptAndSend("sms://" + address);
}
private static boolean isValidPhoneNumber(String number) {
char[] chars = number.toCharArray();
if (chars.length == 0) {
return false;
}
int startPos = 0;
if (chars[0] == '+') {
startPos = 1;
}
for (int i = startPos; i < chars.length; ++i) {
if (!Character.isDigit(chars[i])) {
return false;
}
}
return true;
}
}

Desenvolvimento de Aplicações Móveis 16


JEDITM

4. Exercícios
4.1. Relógio no Celular

Criar um MIDlet que mostre a data e hora atual e que seja atualizada a cada segundo. Utilize um
Timer para atualizar a data e hora e um StringItem para exibi-las.

Desenvolvimento de Aplicações Móveis 17


JEDITM

Parceiros que tornaram JEDITM possível

Instituto CTS
Patrocinador do DFJUG.

Sun Microsystems
Fornecimento de servidor de dados para o armazenamento dos vídeo-aulas.

Java Research and Development Center da Universidade das Filipinas


Criador da Iniciativa JEDITM.

DFJUG
Detentor dos direitos do JEDITM nos países de língua portuguesa.

Banco do Brasil
Disponibilização de seus telecentros para abrigar e difundir a Iniciativa JEDITM.

Politec
Suporte e apoio financeiro e logístico a todo o processo.

Borland
Apoio internacional para que possamos alcançar os outros países de língua
portuguesa.

Instituto Gaudium/CNBB
Fornecimento da sua infra-estrutura de hardware de seus servidores para que os
milhares de alunos possam acessar o material do curso simultaneamente.

Desenvolvimento de Aplicações Móveis 18