Comunicação paralela com servidor em aplicações J2ME com HttpConnection usando componente reutilizável

489.jpgEstarei apresentando neste artigo uma das melhores práticas para fazer a comunicação com o servidor usando HttpConnection em aplicações móveis J2ME, iniciando a conexão com apenas uma linha de código. Ensinarei a criar uma classe reutilizável responsável por toda a comunicação com o servidor, deixando sua aplicação mais lógica, enxuta e melhor estruturada. Você poderá inclusive disparar várias conexões paralelas, e ainda assim, ao receber os dados, terá o controle de qual conexão se trata. Poderá facilmente cancelar a requisição, caso queira. Se aplicar as informações deste artigo em seus projetos, você passará a se preocupar apenas com as regras de negócio, apenas trabalhará com a informação em si, despreocupando-se com quaisquer detalhes quanto à comunicação com o servidor.

No final do artigo você poderá baixar os arquivos do componente de conexão reutilizável, bem como uma aplicação de exemplo que mostra como usar o componente corretamente.

Explicarei brevemente, de início, o funcionamento básico. Como sabemos, uma requisição HttpConnection deve operar numa thread separada, caso contrário a aplicação ficaria travada enquanto sua aplicação estivesse se comunicando com o servidor.

Usuário entra na aplicação, e a tela é mostrada. Usuário clica em Baixar Imagem. Um diálogo é mostrado permitindo que o usuário cancele a requisição a qualquer momento. A tela chama um método estático do componente de requisição, passando a URL e outras informações, iniciando a requisição, e fica aguardando o término. Essa tela implementa o listener que será executado quando a requisição estiver concluida. Quando a requisição estiver concluída, o componente chama o listener na tela, informando que a requisição terminou, e passando os dados. A tela mostra os dados/imagem, e oculta o diálogo de cancelamento da requisição que estava ativo. O usuário fica feliz vendo a imagem vinda do site.

O método estático do componente que inicia a requisição, pede vários parâmetros, como a URL, se é POST ou GET, os parâmetros, o timeout, o objeto que está implementando o listener de resposta da requisição que no nosso exemplo é a própria tela, o id da conexão para que se possa identificá-la facilmente na resposta do listener. Quando esse método é chamado, ele devolve uma instância do componente, e já inicia a thread de conexão por conta própria. É aconselhável que a tela guarde a referência ao componente de requisição, para o caso de o usuário desejar cancelar a requisição. Enquanto a requisição está ativa, fica também ativa um diálogo permitindo que o usuário cancele a conexão. A thread da requisição envia/recebe todos os dados do servidor, e quando termina, chama um listener na tela que iniciou a conexão, avisando-a de que a conexão terminou, devolvendo os dados retornado pela requisição à tela para serem tratados.

Seguindo o conceito acima, criaremos as seguintes classes/interface:

Aplicacao - MIDlet responsável por iniciar a tela.

Tela - Tela que extende a classe Form que será mostrada ao usuário. A tela que será responsável tanto por iniciar a conexão, quanto por receber os dados retornados pela requisição e fazer algo com eles.

RequisicaoHttp - Classe que extende a Thread, e será responsável por todo o processo de comunicação com o servidor, controle de erros, timeout, leitura e envio de dados etc. No final da requisição essa classe chamará o listener da tela, avisando-a de que a requisição terminou. Para ficar mais fácil de usar nosso componente, criaremos um método estático que criará um objeto dessa classe, preparará as propriedades, e iniciará a conexão, de forma que a tela tenha que usar apenas uma linha de código para fazer uma requisição.

ConexaoHttpListener - Interface que terá um método para ser implementandos pela tela. Esse método na tela será chamado quando a requisição for finalizada, para que a tela saiba que a requisição terminou e faça algo com os dados retornados.

Exemplo de início de uma requisição depois que estiver tudo pronto:

public void commandAction(Command comando, Displayable tela) {
    if (comando == comandoInicia)
        RequisicaoHttp requisicaoHttp = RequisicaoHttp.iniciaRequisicao(”http://devmobile.blog.br/feed/”,
        “parametro1=bh”, RequisicaoHttp.POST, this, 0, 20000);
}

Esse método estático cria uma instância do componente, define suas propriedades passadas como parâmetro, e inicia a thread de requisição. Por fim, retorna o próprio objeto instanciado, caso a tela queira guardá-lo numa variável.

Para que a tela receba a resposta da finalização da conexão ela deve implementar a interface que criaremos HttpReqListener. Essa interface terá apenas um método declarado, que informará a tela se a requisição ocorreu tudo bem, os dados retornados pelo servidor e a mensagem de erro caso tenha ocorrido algum erro.

Veja como ficaria um exemplo de implementação desse listener:

public void requisicaoConcluida(boolean sucesso, String mensagemErro, byte[] dadosRecebidos, int id) {
    if (sucesso) {
        String dados = new String(dadosRecebidos);
        textField.setString(dados);
    } else {
        Alert alert = new Alert(mensagemErro);
        AplicacaoMIDlet.app.display.setCurrent(alert, this);
    }
}

E se quiséssemos permitir que o usuário cancele a requisição depois que ela tiver começado, garantindo que todos os objetos e threads serão fechados corretamente? Isso é fácil, pois o componente está preparada para isso. Basta chamar o método cancelaRequisicao() do objeto da requisição. Para isso, é recomendável que antes de iniciar a requisição, seja mostrada um diálogo ao usuário, avisando-o para aguardar a requisição, com um comando para o caso de ele querer cancelar. Quando esse comando for clicado, seria executado algo como:

public void commandAction(Command comando, Displayable tela) {
    if (comando == comandoCancelar) {
        //Usuário pressionou o comando Cancelar, na tela de cancelamento de requisição.
        //Cancela a requisição.
        requisicaoHttp.cancelaRequisicao();
        //Mostra a tela padrão, removendo a tela de diálogo.
        AplicacaoMIDlet.app.display.setCurrent(this);
    }
}

E como poderíamos fazer 3 conexões paralelas, ao mesmo tempo, que retornem informações diferentes com tratamentos diferentes em suas respostas? Quando iniciamos as 3 requisições, definirmos um ID diferentes para cada uma. Esse ID ajudará a identificar a requisição na resposta. Veja:

public void commandAction(Command comando, Displayable tela) {
    if (comando == comandoInicia) {
    	//Requisição do HTML com previsão do tempo, ID = 0
    	RequisicaoHttp requisicaoHttp = RequisicaoHttp.iniciaRequisicao(”http://devmobile.blog.br”, 
        	null, RequisicaoHttp.POST, this, 0, 20000);
    	//Requisição do XML com opções, ID = 1
    	RequisicaoHttp requisicaoHttp = RequisicaoHttp.iniciaRequisicao(”http://devmobile.blog.br”, 
		null, RequisicaoHttp.POST, this, 1, 20000);
    	//Requisição do logo, ID = 2
    	RequisicaoHttp requisicaoHttp = RequisicaoHttp.iniciaRequisicao(”http://devmobile.blog.br”, 
		null, RequisicaoHttp.POST, this, 2, 20000);
    }
}

Para identificar a resposta, que poderá vir sem ordem:

public void requisicaoConcluida(boolean sucesso, String mensagemErro, byte[] dadosRecebidos, int id) {
        if (sucesso) {
            if (id == 0) { //HTML
                String html = new String(dadosRecebidos);
                textField.setString(dados);
        } else if (id == 1) { //XML
                String xml = new String(dadosRecebidos);
                textField.setString(dados);
        } else if (id == 2) { //Imagem
                    Image image = Image.createImage(dadosRecebidos, 0, dadosRecebidos.length);
                    insert(0, new ImageItem(null, image, Item.LAYOUT_CENTER, “Img”, Item.PLAIN));
        } else 
    } else {
            Alert alert = new Alert(mensagemErro);
            AplicacaoMIDlet.app.display.setCurrent(alert, this);
    }
}

Pronto! Facilmente criamos uma requisição totalmente responsável, com controle de timeout, controle de erros, definição de cabeçalhos, etc.

Baixe o exemplo completo, bem comentado, que mostra como usar corretamente o componente, como controlar os erros, como permitir que o usuário cancele a requisição, e muito mais.
Exemplo de requisição Http em J2ME HttpConnection

Baixe os 2 arquivos do componente e use em suas aplicações.
Componente reutilizável para conexão em J2ME usando HttpConnection

Se alguém tiver mais alguma idéia para melhorar o componente, pode postar aqui.

Bons códigos!



Sobre o Autor

Este artigo foi escrito por Nelson Pereira Junior.
Nelson é desenvolvedor há 12 anos. Hoje desenvolve aplicações Web e Móveis na Abacomm Brasil cuidando do desenvolvimento server-side J2EE, banco de dados, design de aplicações móveis, e desenvolvimento móvel usando várias plataformas como BlackBerry, J2ME, FlashLite, Android, etc. Para conversar com o autor use o e-mail, MSN e GTalk npereirajr@gmail.com.



Receba artigos em seu e-mail

Receba os novos artigos do blog em seu e-mail. E-Mail:



19 Comentários

  1. Renato Custódio:

    Muito bom o artigo, parabéns!

  2. Walter Alves Chagas Junior:

    Parabens colega. Seu blog é tudo de bom!!!!

  3. Barreto:

    Fizeste um excelente artigo. Vai ajudar muita gente a entender sobre j2me Httpconnection.

    Valeu

  4. Antônio Júnior:

    Excelente artigo, tirou muitas dúvidas que eu tinha com relação ao HttpConnection. Agora se não for pedir muito, será que você poderia diponibilizar a aplicação servidora que você usou no exemplo?

    Abraços!!

  5. JavaME: Generic Connection Framework « wpjr2’s Weblog:

    […] DevMobile […]

  6. ONILSON:

    Gostei muito do seu artigo,sou iniciante em j2me,atualmente estou quase fechando um projeto com a universidade do meu estado para desenvolver um sistema usando j2me,bom eu gostaria de saber se existe mais fontes de informação sobre a tecnologia j2me na internet ( sites brasileiros)…

    abraços valeu !!!

  7. Nelson Pereira Junior:

    Tem sim, mas não são específicos. Vou citar 2: javafree.com.br e guj.com.br.

  8. Cláudia:

    Muitooooo bom! Eu estava justamente precisando de uma explicação assim sobre requisição. Estou começando a estudar sobre jme, e achei o artigo muito esclarecedor!

    Obrigada!

  9. Caio:

    Muito bom kra, estou fazendo uma aplicação em j2me, esse post vai me ajudar muito…uma sugestão para proximos post ( uma classe para ler arquivos xml em j2me)

  10. sobrinho:

    parabens pelo tutorial de alto nivel.

    Preciso fazer o caminho inverso, enviar strings para o servlet via request.getParameter() e armazenar em banco.
    qual a melhor abordagem no midlet pois no servlet ta relativamente facil.

    muito obrigado e parabens.

  11. sobrinho:

    parabens,
    embora aborde de forma profissional um exemplo trivial vc mostrou muito bem a importancia dos patterns.
    sugiro extender ou reescrever um exemplo que aborde threads automaticas pois e cada vez mais comuns aplicativos operando continuamente em threads independentes tais maquinas conversando com maquinas.

    abs

  12. Ausli Sena:

    Você foi sensacional com seu exemplo, falta apenas o passo a passo do server com JDBC… Abraços

  13. TecoAvila:

    Show de Bola, excelente artigo !!

    Obrigado.

  14. Mobilidade é tudo » Arquivo do Blog » Facilitando a comunciação HTTP no Java ME:

    […] acaso, descobri algumas linhas de códigos que podem facilitar nossa vida, e muito. O post “Comunicação paralela com servidor em aplicações J2ME com HttpConnection usando componente reutil…“, do blog DevMobile, fala sobre duas classes utilitárias que permitem o acesso a um servidor […]

  15. Marcos:

    Isso é gambiarra! Aprende a usar Web Services!

  16. JUNIOR FAE:

    Realmente teu artigo esta muito bom. Gostaria de saber se vc ja implementou alguma coisa usando um http://ftp. Pegando um arquivo do celular e enviando para um servidor ftp e na mesma conexao baixando outro.

    Abraços e parabéns!

    JUNIOR FAE

  17. Nelson:

    FTP? Não! Não faz qualquer sentido uma aplicação se comunicar com um servidor via http://FTP. Se você precisa enviar dados para um servidor, você deve criar uma aplicação no lado servidor, em PHP ou Servlet, para enviar e receber os dados do JME e gravar num banco, por exemplo.

  18. Nelson:

    Marcos, isso não é gambiarra. Usar WebServices não é recomendado usar em aplicações móveis. É custoso para o servidor e extremamente custoso para o mobile. WebServices foi concebido para ser usado num modelo de aplicações B2B, não B2C como é o caso de uma aplicação móvel.

  19. Valdir:

    Parabéns, excelente artigo. Irá ajudar muito quem precisa, me ajudou bastante.

Deixe um comentário

blogarama.com Globe of Blogs EatonWeb Blog Directory