WirelessBR |
WirelessBr é um site brasileiro, independente, sem vínculos com empresas ou organizações, sem finalidade comercial, feito por voluntários, para divulgação de tecnologia em telecomunicações |
Tutorial de programação J2ME |
Autor: Rodrigo P. Garcia Corbera |
Introdução
Como pudemos aprender nos tutoriais anteriores, os dispositivos celulares podem implementar em seu sistema o KVM, a máquina virtual que roda aplicações J2ME. A API de programação para aparelhos celulares está baseada em CLDC e MIDP.
Para maiores detalhes veja o tutorial - parte 1 (http://geocities.yahoo.com.br/brasilwireless/tutorial_j2me/j2me_01/j2me_01.html)
Também pudemos ver um exemplo de aplicação em J2ME que escreve na tela do celular a frase "Hello World!".
O exemplo da aplicação "Hello World!" e as estruturas de programação utilizadas podem ser encontrados no tutorial - parte 2
(http://geocities.yahoo.com.br/brasilwireless/tutorial_j2me/j2me_02/j2me_02.html)
No capítulo 3 do tutorial abordamos como compilar e testar a aplicação "Hello World!" utilizando o Wireless ToolKit que a Sun Microsystems disponibiliza em seu site (para maiores detalhes veja em
http://geocities.yahoo.com.br/brasilwireless/tutorial_j2me/j2me_03_1/j2me_03_1.html).
Neste quarto e último capítulo do tutorial, apresentarei uma versão mais sofisticada do programa "Hello World!", onde mostrarei como fazer animações simples usando caracteres de texto, o que pode ser melhorado, futuramente pelo próprio estudante, conforme alguns exercícios propostos ao final do documento.
Veja um pequeno vídeo
de demonstração do resultado a ser obtido na tela do celular.
O código fonte em JAVA do exemplo aqui descrito tem direitos autorais pertencentes à NOKIA e pode ser encontrado no documento chamado "Brief Introduction to MIDP
Programming".
Partes do aplicativo
O aplicativo é dividido em 4 classes. São elas:
BouncingTextMIDlet
Esta classe é responsável por criar o Midlet (aplicação J2ME) e coordenar a transição entre telas do aplicativo. Existem basicamente duas telas que são apresentada. Uma referente à edição do texto que será animado. Outra que é a própria animação. Cada qual está implementada em uma classe, sendo elas
TextInputScreen e BouncingTextCanvas respectivamente.
No início da execução do aplicativo, criamos uma instância da classe TextInputScreen, para a qual passamos o foco do Display. Com isso a tela que é mostrada é referente a edição de um texto inicial ("Hello J2ME!"). Esse texto poderá ser modificado ou mantido somente no início da execução da aplicação. Uma vez que esteja pronto para ser animado, o usuário pressiona a tecla "Iniciar" e nesse momento o controle que estava com o "TextInputScreen" volta para "BouncingTextMIDlet" através do método "textInputScreenDone". Nesse momento cria-se uma instância da classe "BouncingTextCanvas" e inicia-se a animação.
O controle somente volta para o "BouncingTextMIDlet" quando o usuário seleciona "Sair" durante a animação, forçando-se a desativação do Midlet e finalização da aplicação.
TextInputScreen
Esta classe contem uma tela que apresenta o texto a ser animado, permitindo sua alteração. É uma implementação muito simples que estende a classe "TextBox" de forma a ter uma base já pronta herdada com a finalidade de processar o teclado e mostrar os resultados na tela. O usuário pode selecionar "Sair" para forçar a finalização do aplicativo ou selecionar o comando "Iniciar" que faz o controle de execução voltar para a classe "BouncingTextMIDlet", a qual iniciará em seguida a animação do texto.
BouncingTextCanvas
A classe Canvas aqui utilizada é um "Displayable", assim como outras classes derivadas da classe Screen analisada na parte 2 do tutorial. A diferença é que esta classe é normalmente destinada ao uso de desenhos gráficos e contem uma série métodos para controle e processamento de eventos de entrada da interface com o usuário (teclado, eventos similares ao gerados com mouse ou com uso de touch
screen).
Os métodos herdados dessa classe utilizados neste tutorial são:
1. paint() - chamado a qualquer momento pelo sistema de controle de aplicativos para redesenhar a tela. É ativado por uma ou múltiplas chamada ao método repaint(). O problema é que uma chamada a repaint() somente indica que a tela deve ser redesenhada e não implica em chamar paint() imediatamente (o qual nunca deve ser evocado diretamente!). Portanto usamos o método serviceRepaints() que bloqueia a execução da thread até que o método paint() tenha sido executado. Com isso podemos sincronizar o desenho de cada quadro da animação.
2. keyPressed(), keyRepeated() e keyReleased() - são chamados quando alguma tecla é pressionada. Embora não utilizemos esses métodos no nosso exemplo, eles serão necessários para os exercícios propostos.
Esta classe também implementa a interface "Runable", que lhe confere capacidade de ser executada em uma ou mais linhas de execução ou threads. A linguagem JAVA é nativamente multi-thread. Usando este recurso, podemos simplificar em muito o código executado reduzindo seu tamanho, o que é muito desejado pois o espaço disponível em muitos aparelhos celulares é bem limitado.
O método run() é o corpo da linha de execução, ou thread. Isto significa que quando uma instância da classe "BouncingTextCanvas" é criada, a primeira coisa que acontece é a execução do construtor. Em seguida, temos que criar um novo thread através da chamada a "new Thread(runable_objeto)". Nesse momento é criado uma nova thread, ou linha paralela de execução, cujo código a ser executado em paralelo é o que é definido pelo método run(). Essa nova thread somente será ativada (ou seja o método run será posto para rodar) quando for executado o método Thread.start(). Isto é feito através da execução do método BouncingTextCanvas.startAnimation().
No nosso caso o método run() executa um "loop" infinito que somente é parado através do acionamento do comando "Sair" na interface da aplicação. Esse "loop" executa os seguintes passos:
i. calcula a hora corrente em milisegundos;
ii. atualiza a posição do texto através de uma chamada a tick();
iii. solicita o desenho do texto na nova posição através de repaint() e espera até que o sistema indique que já desenhou a tela atualizada através de
serviceRepaints();
iv. verifica se já se passou mais que 100 ms. Caso não espera até que se passem os 100ms.
v. Volta ao passo (i).
Como então esse "loop" é suspendido? Isto é feito pois a condição de parada do loop é quando a thread em execução não coincidir com a variável animationThread. Isto ocorre quando o usuário aciona "Sair" pois essa variável é mudada para "null". Notem que cada thread da classe "BouncingTextCanvas" acessa as mesmas variáveis da instância criada. Esses dados são compartilhados entre as várias threads criadas a partir de uma mesma instância. No nosso caso criamos apenas uma instância da classe e somente uma thread a partir dessa instância.
Programação com threads exige muito cuidado pois podemos entrar em situações conhecidas como "dead locks" ou "travadas fatais", onde o programa para de rodar por um thread esperar por uma condição que nunca vai ocorrer, a qual depende de outro thread que também espera por uma condição que nunca vai ocorrer no primeiro. É como se tivéssemos um problema de trânsito em um semáforo como abaixo:
Supondo que os carros somente podem seguir a direção das flechas, temos uma ilustração de um problema de "dead lock", onde tudo fica travado onde está.
Existe um segundo possível problema em execuções multi-thread que é o sincronismo ou controle do acesso a dados compartilhados ou métodos compartilhados. Como não é possível prever onde a thread será paralisada para passar o controle de execução a outra thread, devemos utilizar o modificador synchronized para atributos ou métodos ou synchronized(objeto) {} para blocos de código. Dessa forma garantimos que somente uma thread por vez acessará aquele atributo ou método ou mesmo executará um bloco de código por vez. Não havendo assim problemas de acesso "simultâneo" a essas áreas.
Com isso dito, podemos verificar que a classe "BouncingTextCanvas" cuida da execução da animação a uma taxa constante de quadros, através dos métodos run() e tick(), permite iniciar ou finalizar threads através dos métodos startAnimation() e stopAnimation() e implementa o método paint() que desenha cada quadro da animação.
Um objeto (instância) da classe "BouncingTextCanvas" pode conter um ou mais objetos do tipo "BouncingText". No nosso exemplo apenas controla um objeto desse tipo, portanto um único texto animado. Seria possível fazê-lo controlar mais que um...
BouncingText
Classe que controla um objeto animado da tela. Um objeto dessa classe tem atributos específicos para ser animado, tais como posição na tela, o próprio texto a ser mostrado, direção e velocidade em um dado momento.
O método updatePosition(), garante que quando o texto ao ser animado atinge as bordas da tela, muda de direção de forma a se manter dentro dos limites da tela. Também condiciona que o movimento gerado não force com que o texto seja desenhado fora da tela. Para isso calcula o tamanho do texto em pixels e a cada interação verifica se a caixa imaginária que contem o texto estará dentro ou não da tela.
Outro método importante é o draw() que é chamado de dentro do BoucingTextCanvas.paint() de forma a desenhar o texto na última posição atualizada.
Por últimos temos um método que inverte o texto (para "otxet" por exemplo) quando comandado via interface com o usuário.
[Continua]