P. 1
Projeto JEDI - Desenvolvimento de Aplicações Móveis - Java - 164 páginas

Projeto JEDI - Desenvolvimento de Aplicações Móveis - Java - 164 páginas

5.0

|Views: 442|Likes:
Publicado poraugustonunes

More info:

Published by: augustonunes on Feb 04, 2010
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

05/07/2013

pdf

text

original

Módulo 5

Desenvolvimento de Aplicações Móveis

Lição 1
Introdução

Versão 1.0 - Set/2007

JEDITM

Autor A. Oliver de Guzman Equipe Rommel Feria John Paul Petines

Necessidades para os Exercícios

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 • Red Hat Fedora Core 3 • Solaris™ 10 Operating System (SPARC® e x86/x64 Platform Edition) 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 Microsoft Windows Linux Solaris OS (SPARC) Solaris OS (x86/x64 Platform Edition) Mac OS X Processador 500 MHz Intel Pentium III workstation ou equivalente 500 MHz Intel Pentium III workstation ou equivalente UltraSPARC II 450 MHz AMD Opteron 100 Série 1.8 GHz PowerPC G4 Memória 512 MB 512 MB 512 MB 512 MB 512 MB HD Livre 850 MB 450 MB 450 MB 450 MB 450 MB

Configuração Recomendada de Hardware Sistema Operacional Microsoft Windows Linux Solaris OS (SPARC) Solaris OS (x86/x64 Platform Edition) Mac OS X Processador 1.4 GHz Intel Pentium III workstation ou equivalente 1.4 GHz Intel Pentium III workstation ou equivalente UltraSPARC IIIi 1 GHz AMD Opteron 100 Series 1.8 GHz PowerPC G5 Memória 1 GB 1 GB 1 GB 1 GB 1 GB HD Livre 1 GB 850 MB 850 MB 850 MB 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 Alexandre Mori Alexis da Rocha Silva Allan Souza Nunes Allan Wojcik da Silva Anderson Moreira Paiva Andre Neves de Amorim Angelo de Oliveira Antonio Jose R. Alves Ramos Aurélio Soares Neto Bruno da Silva Bonfim Carlos Fernando Gonçalves Denis Mitsuo Nakasaki Fábio Bombonato Fabrício Ribeiro Brigagão Francisco das Chagas Frederico Dubiel Herivelto Gabriel dos Santos Jacqueline Susann Barbosa João Vianney Barrozo Costa Kefreen Ryenz Batista Lacerda Kleberth Bezerra G. dos Santos Leandro Silva de Morais Leonardo Ribas Segala Lucas Vinícius Bibiano Thomé Luciana Rocha de Oliveira Luiz Fernandes de Oliveira Junior Marco Aurélio Martins Bessa Maria Carolina Ferreira da Silva Massimiliano Giroldi Mauro Cardoso Mortoni Paulo Afonso Corrêa Paulo Oliveira Sampaio Reis Pedro Henrique Pereira de Andrade Ronie Dotzlaw Seire Pareja Sergio Terzella Vanessa dos Santos Almeida 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 JavaCard móveis e dispositivos de mão Cartões com chip

Servidores Servers Máquinas desktop
Pacotes Opcionais Pacotes Opcionais

Dispositivos de alto consumo

Dispositivos de baixo consumo Cartões com chip

Java EE Java SE

Personal Profile Foundation Profile CDC MIDP CLDC Java Card

Máquina Virtual Java

KVM

Card VM

Java Micro Edition (Java ME)
Figura Desenvolvimento de Aplicações Móveis

1: A plataforma

Java 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 Perfis Configuração Pacotes Opcionais { OEM APIs

Bibliotecas 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 verificar (execução)

compilar (javac)

File.class

pré-verificar

interpretar

File.class

Máquina de Desenvolvimento
Figura

Dispositivo

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 superconjunto 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 MIDP 2.0 WMA 1.1 MMAPI Pacotes 1.1 Opcionais OEM 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 dispositivos móveis?
• • •

para

escrever

para

programas

para

os

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 somente uma única vez na vida de um programa, o método uma vez durante o ciclo de vida do MIDlet. Então, não se única no método startApp(). Para isso, deve ser criado 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

main() é executado automaticamente startApp() pode ser chamado mais de deve colocar códigos de inicialização um construtor para o MIDlet e as

JEDITM

} public } public } public if }

void pauseApp() { void destroyApp(boolean unconditional) { void commandAction(Command c, Displayable d){ (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 Altamente portátil entre aparelhos Aparência é a mesma nos aparelhos Interfaces Gráficas de Baixo Nível Pode ser específico do dispositivo 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 Não são acessadas por aparelhos com características específicas Definem a aparência em nível de pixel O acesso ao baixo nível de entrada é por 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 Label Commands defaultCommand ItemCommandListener Layout directive Preferências de altura e largura nenhum nulo nulo LAYOUT_DEFAULT -1 (destravado) Valor Padrão Especificado no construtor da subclasse

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
Figura 8: Diferentes tipos de Alert

Alert com o indicador gauge

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
Figura 9: Tipos de lista

Lista MULTIPLE

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
Figura 13: Modelos de DateField e telas de entrada

Selecionando Horas

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.NUMERIC TextField.URL TextField.EMAILADDR TextField.PHONENUMBER TextField.DECIMAL

A entrada pode também ter estes modificadores: TextField.PASSWORD TextField.SENSITIVE TextField.INITIAL_CAPS_WORD TextField.UNEDITABLE TextField.NON_PREDICTIVE 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 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 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.
(0,0)

increasing x value

increasingy value (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) keyRepeated(int keyCode) keyReleased(int keyCode) Quando uma tecla é pressionada Quando uma tecla é repetida (quando é mantida pressionada) 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() public int getRedComponent() public int getGreenComponent() public int getBlueComponent() public int getGrayScale() public void setGrayScale( int value) retornar um inteiro da cor atual do formulário no formato 0x00RRGGBB retornar o valor da cor vermelho do componente atual retornar o valor da cor verde do componente atual retornar o valor da cor azul do componente atual retornar o valor da escala de cinza da cor atual definir o valor da escala de cinza para uma próxima 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( int face, int style, int size) retornar um objeto do tipo Font do sistema com 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 public public public public public public int getStyle() int getFace() int getSize() boolean isPlain() boolean isBold() boolean isItalic() 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( String str, int x, int y, int anchor) public void drawSubstring( String str, int offset, int len, int x, int y, int anchor) public void drawChar( char character, int x, int y, int anchor) public void drawChars( char[] data, int offset, int length, int x, int y, int anchor) desenhar o texto definido pelo parâmetro str utilizando a cor e fonte atual. Os parâmetros x e y são as coordenadas do ponto inicial desenhar de maneira semelhante ao método drawString, exceto pelos parâmetros offset que limita a base-zero e len que é responsável pelo tamanho

desenhar o caractere usando a cor da fonte atual

desenhar os caracteres contidos no parâmetro data, iniciando pelo índice definido no parâmetro offset com o tamanho especificado em length

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
12

Desenvolvimento de Aplicações Móveis

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) public int getBaselinePosition()

retornar a largura total em pixels do espaço ocupado pelo parâmetro ch se for escrito utilizando-se esta fonte 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( int x, int y, int largura, int altura, int anguloInic, int anguloArco) public void fillArc( int x, int y, int largura, int altura, int anguloInic, int anguloArco)

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

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 VCENTER | HCENTER

BOTTOM | RIGHT 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

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

}

} 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 1 2 3 5 Array de bytes Dado do registro #1 Dado do registro #2 Dado do registro #3 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() int getNumRecords() int getSize() int getSizeAvailable() void setMode( int authmode, boolean writable)

Retornar o recordId do próximo registro a ser adicionado ao objeto RecordStore Retornar o número de registros atualmente no RecordStore Retornar a quantidade de bytes ocupados em um RecordStore Retornar a quantidade de bytes disponíveis em um RecordStore Modificar o modo de acesso de um RecordStore

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

} 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

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

JEDITM

}

} }

}

} } class InsertForm extends Form { private TextField QTDGAS; private TextField ODOMETER;

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

}

}

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 GET /index.jsp?id=100 HTTP/1.1 Servidor HTTP 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 GET /non-existent.html HTTP/1.0

Servidor HTTP 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 BROKEN_CHAIN CERTIFICATE_CHAIN_TOO_LONG EXPIRED INAPPROPRIATE_KEY_USAGE MISSING_SIGNATURE NOT_YET_VALID ROOT_CA_EXPIRED SITENAME_MISMATCH UNAUTHORIZED_INTERMEDIATE_CA UNRECOGNIZED_ISSUER UNSUPPORTED_PUBLIC_KEY_TYPE UNSUPPORTED_SIGALG VERIFICATION_FAILED Indicar que um certificado possui extensões críticas desconhecidas Indicar que um certificado numa cadeia não foi gerado pela próxima autoridade na cadeia Indicar que o tamanho da cadeia de servidores certificados excedeu o tamanho permitido pela política do emissor Indicar que um certificado expirou Indicar que a chave pública do certificado foi usada de maneira considerada inapropriada pelo emissor Indicar que um objeto certificado não contém uma assinatura Indicar que um certificado ainda não é válido Indicar que a chave pública da autoridade certificadora raiz (root CA) expirou Indicar que um certificado não contém o nome correto do site Indicar que um certificado intermediário na cadeia não possui a permissão para ser uma autoridade certificadora intermediária Indicar que um certificado foi emitido por uma entidade desconhecida Indicar que o tipo da chave pública no certificado não é suportado pelo dispositivo Indicar que um certificado foi assinado utilizando um algoritmo não suportado 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 import import import java.io.*; javax.microedition.io.*; javax.microedition.lcdui.*; 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

}

} 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) { } }

ioe.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); }

Classe Sender.java: package socket; import import import import java.io.*; javax.microedition.io.*; javax.microedition.lcdui.*; 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

}

} } public synchronized void stop() { message = null; notify(); }

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

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 import import import java.io.*; javax.microedition.io.*; javax.microedition.lcdui.*; 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 import import import java.io.*; javax.microedition.io.*; javax.microedition.lcdui.*; 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

}

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

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

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 import import import java.io.*; javax.microedition.io.*; javax.microedition.lcdui.*; javax.microedition.midlet.*;

public class Client implements Runnable, CommandListener { private private private private private private private private ClientDatagramMidlet parent; Display display; Form f; StringItem si; TextField tf; Command sendCommand = new Command("Send", Command.ITEM, 1); Command exitCommand = new Command("Exit", Command.EXIT, 1); 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 A. Oliver de Guzman Equipe Rommel Feria John Paul Petines

Necessidades para os Exercícios

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 • Red Hat Fedora Core 3 • Solaris™ 10 Operating System (SPARC® e x86/x64 Platform Edition) 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 Microsoft Windows Linux Solaris OS (SPARC) Solaris OS (x86/x64 Platform Edition) Mac OS X Processador 500 MHz Intel Pentium III workstation ou equivalente 500 MHz Intel Pentium III workstation ou equivalente UltraSPARC II 450 MHz AMD Opteron 100 Série 1.8 GHz PowerPC G4 Memória 512 MB 512 MB 512 MB 512 MB 512 MB HD Livre 850 MB 450 MB 450 MB 450 MB 450 MB

Configuração Recomendada de Hardware Sistema Operacional Microsoft Windows Linux Solaris OS (SPARC) Solaris OS (x86/x64 Platform Edition) Mac OS X Processador 1.4 GHz Intel Pentium III workstation ou equivalente 1.4 GHz Intel Pentium III workstation ou equivalente UltraSPARC IIIi 1 GHz AMD Opteron 100 Series 1.8 GHz PowerPC G5 Memória 1 GB 1 GB 1 GB 1 GB 1 GB HD Livre 1 GB 850 MB 850 MB 850 MB 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 Alexandre Mori Alexis da Rocha Silva Allan Souza Nunes Allan Wojcik da Silva Anderson Moreira Paiva Andre Neves de Amorim Angelo de Oliveira Antonio Jose R. Alves Ramos Aurélio Soares Neto Bruno da Silva Bonfim Carlos Fernando Gonçalves Denis Mitsuo Nakasaki Fábio Bombonato Fabrício Ribeiro Brigagão Francisco das Chagas Frederico Dubiel Herivelto Gabriel dos Santos Jacqueline Susann Barbosa João Vianney Barrozo Costa Kefreen Ryenz Batista Lacerda Kleberth Bezerra G. dos Santos Leandro Silva de Morais Leonardo Ribas Segala Lucas Vinícius Bibiano Thomé Luciana Rocha de Oliveira Luiz Fernandes de Oliveira Junior Marco Aurélio Martins Bessa Maria Carolina Ferreira da Silva Massimiliano Giroldi Mauro Cardoso Mortoni Paulo Afonso Corrêa Paulo Oliveira Sampaio Reis Pedro Henrique Pereira de Andrade Ronie Dotzlaw Seire Pareja Sergio Terzella Vanessa dos Santos Almeida 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 import import import java.io.*; java.net.*; javax.servlet.*; 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 core I18N formatting XML processing Database (SQL) Functions Prefixo c fmt x sql fn URI http://java.sun.com/jstl/core http://java.sun.com/jstl/fmt http://java.sun.com/jstl/xml http://java.sun.com/jstl/sql http://java.sun.com/jstl/functions Exemplo de código <c:out value="${var}"/> <fmt:formatNumber <x:forEach ... <sql:query var=... <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 import import import java.io.*; java.net.*; javax.servlet.*; 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 Implícito pageScope requestScope sessionScope applicationScope param header cookie 3.4.3. Conteúdo
Contém um mapeamento de nomes de atributos de escopo de página para seus valores Contém um mapeamento de nomes de atributos de escopo de requisição para seus valores Contém um mapeamento de nomes de atributos de escopo de sessão para seus valores Contém um mapeamento de nomes de atributos de escopo de aplicação para seus valores Contém um mapeamento de nomes de parâmetros para seus valores de parâmetros (cadeia de caracteres). Equivalente a ServletRequest.getParameter(String) Contém um mapeamento de nomes de cabeçalho para seus valores (cadeia de caracteres). Equivalente a servletRequst.getHeader(String) Contém contendo um mapeamento de nomes de "cookies" para seus valores. Equivalente a HttpServletRequest.getCookie(String)

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 && ou and || ou or ! ou not + (adição) - (subtração) * (multiplicação) / (divisão) % ou mod (resto da divisão ou módulo)

Os operadores lógicos suportados são:
● ● ●

E os operadores aritméticos suportados são:
● ● ● ● ●

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 value escapeXml default Exemplos:
Desenvolvimento de Aplicações Móveis 12

Dinâmico Requisito sim sim sim sim não não

Tipo Objeto

Descrição
A expressão a ser avaliada

Se verdadeiro, os caracteres <, >, &, ' e " são convertidos em seus códigos da entidade do caráter boolean (por exemplo: > conversos ao &gt;). O valor padrão é true.

Objeto

O valor padrão se o valor do resultado for nulo

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 value var scope Exemplo: <c:set var="fullName" value="${lastName, firstName}" /> 3.5.3. Tag <c:remove> Dinâmico Requisito Tipo Descrição sim sim Objeto A expressão a ser avaliada não não sim não String expressão. O tipo do atributo segue o tipo do
resultado da expressão O nome do atributo exportada que conterá o valor da

String O escopo do atributo

Esta tag remove um atributo de um determinado escopo. Sintaxe: <c:remove var="varName" [scope="{page|request|session|application}"] /> Nome var scope 3.5.4. Dinâmico Requisito Tipo Descrição O nome do atributo a ser eliminado não sim String não não String O escopo do atributo

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 test var scope Exemplo:

Dinâmico sim não não

Requerido sim não/sim não

Tipo boolean String String

Descrição
A condição testada que determina se o conteúdo do corpo deverá ser processado Nome do atributo exportado que irá conter o valor da condição testada. O tipo do atributo de escopo é Boolean Escopo do atributo

<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 test 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> Dinâmico sim Requerido sim Tipo boolean Descrição
A condição de teste que determina se o conteúdo do corpo deverá ser processado

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 var items Dinâmico Requerido não sim não sim/não Tipo variável Descrição
O nome de um atributo exportado para cada elemento de uma coleção

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

varStatus begin end step

não sim sim sim

não não/sim não/sim nã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 var items delims varStatus Dinâmico Requerido não sim sim não não sim sim não Tipo String String String String Descrição
O nome do atributo exportado para toda instrução iteração e símbolo Literal de símbolos Delimitadores, caracteres que separam os símbolos no literal de itens O nome do atributo exportado para o status da 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 especificado, a iteração vai começar com o primeiro símbolo. Final do índice da iteração O laço será processado por um intervalo definido de itens da iteração

begin end step

sim sim sim

não não não

int int int

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

} statement.executeUpdate(updateQuery);

+ + + + +

"'" + "'" + "'" + "'" + ")";

task + "'," duration + "'," assignedTo + "'," status + "'"

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

}

} } String[] itemArr = new String[items.size()]; items.copyInto(itemArr); return itemArr; } catch( kXMLParseException ke ){ return(null); }

} items.addElement(id + ": " + fields[0]);

}

}

}

fields[p] = prop.getContents();

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 connection accept-language host accept-charset user-agent accept keep-alive gzip,deflate keep-alive en-us,en;q=0.5 localhost:8084 ISO-8859-1,utf-8;q=0.7,*;q=0.7 Mozilla/5.0 (Linux; U; Linux v0.99; en-US) Gecko/20050511 Firefox/ 1.2.3 text/xml,application/xml,application/xhtml +xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 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 String a, b, c; ... String message = "a=" + a + "\n" + "b=" + b + "\n" + "c=" + c + "\n"; StringBuffer String a, b, c; ... StringBuffer message = new StringBuffer(255); message.append("a="); message.append(a); 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] tones[1] tones[2] tones[3] tones[4] tones[5] tones[6] tones[7] = = = = = = = = 64; 63; 65; 76; 45; 56; 44; 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 microedition.media.version supports.mixing Descrição
A versão da especificação MMAPI implementada pelo dispositivo. Exemplo: "1.1" 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. Retorna "true" se a captura de áudio é suportada. Retorna "true" se a captura de vídeo é suportada. Retorna "true" se a gravação de áudio é suportada.

supports.audio.capture supports.video.capture supports.recording

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 import import import import

javax.microedition.midlet.*; javax.microedition.lcdui.*; javax.microedition.media.*; javax.microedition.media.control.*; 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 import import import import javax.microedition.midlet.*; javax.microedition.lcdui.*; javax.microedition.media.*; javax.microedition.media.control.*; java.io.*;
6

Desenvolvimento de Aplicações Móveis

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 import import import import

javax.microedition.midlet.*; javax.microedition.lcdui.*; javax.microedition.media.*; javax.microedition.media.control.*; 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 import import import

javax.microedition.midlet.*; javax.microedition.lcdui.*; javax.microedition.io.*; 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 import import import javax.microedition.midlet.*; javax.microedition.lcdui.*; javax.microedition.io.*; 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( TimerTask task, long delay) void schedule( TimerTask task, long delay, long period) void schedule( TimerTask task, Date time) void schedule( TimerTask task, Date time, long period) void cancel() import import import import import import Agendar a tarefa para que seja executada a cada período (em milissegundos). Agendar a tarefa para que ela seja repetida, começando após o período especificado (em milissegundos). Agenda a tarefa para que seja executada num tempo específico. Agenda a tarefa para que ela seja repetida, começando pelo tempo especificado. Interrompe o timer e descarta qualquer tarefa agendada.

javax.microedition.midlet.*; javax.microedition.lcdui.*; java.io.*; java.util.Timer; java.util.TimerTask; 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

} class CounterTask extends TimerTask { int counter = 0; TimerMidlet midlet;

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

TimerTask task = new CounterTask(this); timer.schedule(task, 2000, 1000); form = new Form("Timer Test"); form.addCommand(exitCommand); form.append(textField);

}

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 import import import javax.microedition.io.*; javax.microedition.lcdui.*; javax.wireless.messaging.*; 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 import import import import javax.microedition.midlet.*; javax.microedition.io.*; javax.microedition.lcdui.*; javax.wireless.messaging.*; 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

} } 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

done = false; thread = new Thread(this); thread.start();

JEDITM

} Midlet para enviar mensagem SMS import import import import import javax.microedition.midlet.*; javax.microedition.io.*; javax.microedition.lcdui.*; javax.wireless.messaging.*; 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

You're Reading a Free Preview

Descarga
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->