sexta-feira, 25 de fevereiro de 2011

Web Services no Android

Olá povo,

No artigo anterior, apresentei como criar Web Services com o Apache Axis2 no Eclipse EE com o plugin WTP. Nesse post mostrarei como acessar esse WebService através de uma aplicação Android.

Um ótimo framework para acessar web services é o KSOAP. Ele é bem conhecido dos desenvolvedores Java ME, uma vez que era a maneira mais fácil de se acessar web services em celulares MIDP. Vamos utiliza-lo no Android através da sua versão para Java SE. Ela pode ser obtida aqui.
Crie um novo projeto Android (que nomearei de BlogWSClient) e adicione o JAR ao projeto. Isso deve ser feito criando uma pasta chamada lib na raiz do projeto e copiando o JAR para lá. Em seguida, adicione esse JAR ao classpath do seu projeto.
Uma vez que iremos acessar um serviço Web, precisamos da permissão android.permission.INTERNET. Sendo assim, o próximo passo será adiciona-la ao nosso aplicativo através da tag uses-permission que deve ser adicionada ao nosso AndroidManifest.xml conforme abaixo.


<uses-permission android:name="android.permission.INTERNET"/>


Agora vamos para o código da nossa Activity. Não vou me preocupar com a parte da interface gráfica, vou apenas mostrar a utilização dos objetos KSOAP.
Tenha sempre esse princípio básico em mente: TODA comunicação de rede deve ser feita com uma Thread separada da Thread principal da aplicação.
Sendo assim, faremos com que nossa classe implemente a interface java.lang.Runnable e no método onCreate, criamos e inicializamos uma nova thread passando a própria instância da Activity como parâmetro. Com isso, o método run chamará dois métodos que, por sua vez, farão a requisição aos nossos serviços.

package ngvl.android.blogws;

// omiti os imports... :)

public class PrincipalActivity
extends Activity
implements Runnable {

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

new Thread(this).start();
}

public void run() {
chamaHelloWS();
chamaPessoaWS();
}

// Adicione os próximos métodos aqui
}

Agora vamos ver a chamada ao serviço HelloWS. Iniciamos com a criação de um SoapObject que representa o namespace do serviço e o método que queremos chamar. Isso está definido no WSDL do nosso serviço, procure pelas propriedades xml:ns e wsdl:operation respectivamente. Em seguida, passamos o parâmetro que o serviço requer, que no exemplo é o nome da pessoa. As requisições SOAP são feitas através de envelopes. Esse objeto é criado e atrelado ao primeiro que criamos.
A variável url armazena o caminho do nosso serviço. Observe que estamos utilizando o IP 10.0.2.2 que permite com que o emulador acesse a máquina local. Se você usa localhost estará tentando acessar o próprio emulador. No aparelho, você deve colocar o endereço real do seu servidor.
Feita a requisição, é só pegar a resposta a partir do envelope.


private void chamaHelloWS() {
SoapObject soap = new SoapObject(
"http://blogws.jee.ngvl", "digaOla");

soap.addProperty("nome", "glauber");

SoapSerializationEnvelope envelope =
new SoapSerializationEnvelope(SoapEnvelope.VER11);

envelope.setOutputSoapObject(soap);

Log.i("NGVL", "Chamando HelloWS");

String url =
"http://10.0.2.2:8080/BlogWS/services/HelloWS";

HttpTransportSE httpTransport =
new HttpTransportSE(url);

try {
httpTransport.call("", envelope);

Object msg = envelope.getResponse();

Log.i("NGVL", "Mensagem: " + msg);

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


O método que acessa PessoaWS é bem similar ao anterior, o que muda é apenas a resposta, uma vez que não mais temos um só retorno, e sim uma lista deles. essa lista é obtida através da propriedade bodyIn do envelope. Então é só percorrer a lista que é retornada.


private void chamaPessoaWS() {
SoapObject soap = new SoapObject(
"http://blogws.jee.ngvl", "obterPessoas");

SoapSerializationEnvelope envelope =
new SoapSerializationEnvelope(SoapEnvelope.VER11);

envelope.setOutputSoapObject(soap);

Log.i("NGVL", "Chamando PessoasWS");

String url =
"http://10.0.2.2:8080/BlogWS/services/PessoaWS";

HttpTransportSE httpTransport =
new HttpTransportSE(url);

try {
httpTransport.call("", envelope);

SoapObject results =
(SoapObject) envelope.bodyIn;

int count = results.getPropertyCount();

for (int i = 0; i < count; i++) {
SoapObject obj = (SoapObject)results.getProperty(i);

Log.i("NGVL",
"Nome: "+ obj.getProperty("nome"));

Log.i("NGVL",
"Idade: "+ obj.getProperty("idade"));
}
} catch (Exception e) {
e.printStackTrace();
}
}


Bem pessoal, espero que vocês tenham gostado. Esse foi um post relâmpago :) dois numa noite só.

4br4ç0s,
nglauber

Web Service com Apache Axis2

Olá povo,

Todo mundo já sabe da atual importância dos Web Services no mundo da TI. Eles promovem a integração entre sistemas inclusive que podem inclusive utilizar tecnologias heterogêneas. Mas como criar esse tipo de software? Bem, como diria a velha propaganda, "existem mil maneiras de se fazer Neston". Traduzindo pra o nosso contexto, existem mil maneiras de se fazer web services. Podemos utilizar os protocolos JSON ou SOAP (pelo menos que eu conheço), e com esses dois podemos desenvolver em várias tecnologias (.net, Java, PHP, ...) , e por fim, para essas tecnologias podemos utilizar alguns frameworks disponíveis no mercado.

Nesse post, que é baseado nesse tutorial de Lars Vogel, apresentarei um exemplo da disponibilização de um Web Service utilizando o padrão SOAP, criado com a linguagem Java e usando o framework Apache Axis2. Vamos utilizar o Eclipse EE com WTP (Web Tools Platform) e as ferramentas Tomcat 6.0 e obviamente do Axis 2. Baixe essas ferramentas e mãos a obra!

Devemos incialmente configurar o Eclipse indicando onde decompactamos o Axis2. Para isso, vá até o menu Window > Preferences. No lado esquerdo, selecione Web Services > Axis 2 Preferences, e na aba Axis 2 Runtime indique o diretório do Axis 2. Ainda na opção Web Services, selecione a subopção Server and Runtime. Então, selecione Tomcat v6.0 Server e Apache Axis 2 nos campos Server e Web service runtime respectivamente.

Crie um novo Dynamic Web Project, vou dar o nome de BlogWS. Uma vez com o projeto criado, adicione um novo pacote, nomearei como ngvl.jee.blogws. Inciaremos criando um serviço bem simples que simplesmente dirá um olá com a hora do servidor. Crie uma classe chamada HelloWS conforme abaixo:

package ngvl.jee.blogws;

import java.text.SimpleDateFormat;
import java.util.Date;

public class HelloWS {
public String digaOla(String nome){
String result;
SimpleDateFormat df =
new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");

Date date = new Date(System.currentTimeMillis());
result = df.format(date);

return "Olá "+ nome +" a hora atual é "+ result;
}
}

Criada a classe, clique com o botão direito sobre a classe e selecione a opção Web Services > Create Web service. A tela abaixo será exibida:


Selecione Web service runtime: Apache Axis2 e clique em Next. Na tela que abrirá em seguida, selecione Generate default services.xml file e clique em Finish.

Feito isso seu WebService está pronto! Mas já? :) Pois é. O Axis2 é bem simples. E com uma ajudinha do WTP fica mais fácil ainda. Pra ver seu serviço rodando, basta clicar com o botão direito sobre o projeto e escolher a opção Run as > Run on server. O browser deve exibir esse endereço (se não exibir digite-o): http://localhost:8080/BlogWS/axis2-web/. Será exibida uma tela de boas vindas do Axis2. Clique no link Services e será exibido os serviços disponíveis. Entre eles está o nosso magistoso HelloWS :) com a operação digaOla. Se você clicar sobre o nome do serviço verá o WSDL (Web Service Descriptor Language) que é o XML que define nosso WS. Nele temos detalhes sobre o serviço, como as operações que estamos disponibilizando, tipos de parâmetros de entrada (response) e de retorno (response).
Mostraremos no próximo post como acessar esse serviço através de uma aplicação Android. Mas antes vamos fugir a regra. Milhões de tutoriais na internet mostram como fazer um serviço simples, que não envolve tipos complexos. Vamos mostrar como criar um serviço que retorna uma lista de objetos do tipo Pessoa.
Primeiro crie uma interface que vai representar seu objeto complexo.


package ngvl.jee.blogws;

public interface Pessoa {
void setNome(String nome);
String getNome();
void setIdade(int idade);
int getIdade();
}

Depois crie a classe que implementa essa interface:

package ngvl.jee.blogws;

public class PessoaImpl implements Pessoa {
private int idade;
private String nome;

public PessoaImpl(String nome, int idade) {
setIdade(idade);
setNome(nome);
}

public int getIdade() {
return idade;
}

public String getNome() {
return nome;
}

public void setIdade(int idade) {
this.idade = idade;
}

public void setNome(String nome) {
this.nome = nome;
}
}


Feito isso, criaremos agora o serviço que retornará uma lista fake de objetos do tipo Pessoa. No mundo real, esse serviço faria uma busca no banco de dados. Mas não vamos colocar isso aqui :)

public class PessoaWS {

public Pessoa[] obterPessoas(){
Pessoa[] pessoas = new Pessoa[]{
new PessoaImpl("Nelson", 27),
new PessoaImpl("Glauber", 18),
new PessoaImpl("Leal", 65)
};
return pessoas;
}
}

Agora gere o WebService a partir dessa última classe, e em seguida execute-a conforme fizemos no exemplo anterior. O resultado ficará como na imagem abaixo:


Pronto! Temos dois serviços HelloWS e PessoaWS publicados. No próximo artigo mostrarei como acessá-los com uma aplicação Android.

4br4ç0s,
nglauber

terça-feira, 8 de fevereiro de 2011

Hello World iPhone

Olá povo,

A muito tempo que deveria ter escrito esse post. Passei 2010 entre iPhone e Android graças ao meu mestrado, e hoje vou apresentar pra vocês como começar a desenvolver aplicativos para o iOS, o sistema operacional dos dispositivos móveis da Apple (iPhone, iPad e iPod touch).
Para começo de conversa, precisamos de um computador que rode o Mac OS X. Pelas vias legais, você deve ter um Macbook, iMac, Mac Pro ou Mac mini. Entretanto você pode recorrer a um bom e velho Hackintosh ou instalar uma máquina virtual com o sistema operacional da Apple.
O kit de desenvolvimento está disponível no site da Apple (developer.apple.com) e pode ser baixado livremente, bastando apenas fazer o registro gratuitamente. Ele só é "um pouquinho" grande, tem cerca de 3.6GB, então se for baixar via dial-up, esqueça! :) (não que eu acredite que um desenvolvedor ainda acesse internet assim). A instalação é bem simples, basta seguir o assistente. A última versão do SDK exige a versão 10.6.4 ou superior do Mac OS, e uma recomendação é que sua máquina tenha 2GB de memória pra trabalhar bem.

As principais ferramentas do SDK são: Xcode, Interface Builder e os Emuladores do iPhone e iPad.
A primeira, é a IDE, onde criamos e gerenciamos nossos projetos, além de escrever o código fonte, compilar e executar a aplicação. O Interface Builder é um editor visual onde podemos criar as telas da aplicação e fazer a associação de componentes visuais para atributos da classe (criada no Xcode), e de eventos disparados por componentes a métodos declarados no código fonte. Quando o código fonte é compilado, ele pode ser testado no emulador. Na versão atual, temos emuladores para iPhone e iPad, porém funcionalidades como acelerômetro e GPS não estão disponíveis neles. Sendo assim, para testar esse tipo de funcionalidade precisamos de um aparelho real, e para tal, você deve pagar por uma licença de desenvolvedor. Com esse licença, você obtém uma assinatura que deve ser usada em suas aplicações de modo a instalá-las no aparelho. Mas se você já fez o Jailbreak no seu iPhone, podem testar sem licença mesmo :)

Nosso exemplo fará um bom e velho HelloWorld. Para começãr, execute o Xcode e vá até o menu File > New project. Será exibida a tela abaixo:

O Xcode disponibiliza alguns templates que criam a estrutura básica do projeto dependendo de sua funcionalidade. Utilizaremos o template "View based Application", selecione-o e clique em Choose. Em seguida, dê o nome ao seu projeto (eu coloquei ExemploBlog) e clique em ok.
O nosso projeto será exibido no Xcode conforme a imagem abaixo.


No lado direito é exibido o conteúdo de uma pasta na parte superior e o conteúdo do arquivo na parte inferior. No lado esquerdo fica a estrutura do nosso projeto organizado em pastas. As principais são as pastas Classes e Resources. Na primeira ficam os arquivos de código fonte, que assim como em C e C++ fica divido em arquivos de cabeçalho (.h) e de implementação (.m). Já as imagens e os arquivos que definem a interface gráfica (chamados de NIBs mas com extensão .xib) ficam no diretório Resources.

Vamos resumir como um programa iOS começa. Ele inicia com um arquivo chamado main.m (que fica na pasta Other Resources) que lê um arquivo que tem o mesmo nome do projeto e com extensão .plist. Nesse arquivo, existe a definição de qual será o NIB principal da aplicação. Nesse NIB constam: uma referência para a classe que implementa UIApplicationDelegate; uma instância de UIWindow que representa a tela do aparelho; e opcionalmente uma referência para UIViewController que trata os eventos de interface gráfica.

Vejam que nosso projeto tem a classe ExemploBlogAppDelegate. Que tem o seguinte código:

// Arquivo .h
#import <UIKit/UIKit.h>

@class ExemploBlogViewController;

@interface ExemploBlogAppDelegate : NSObject
<UIApplicationDelegate>{

UIWindow *window;
ExemploBlogViewController *viewController;
}

@property (nonatomic, retain) IBOutlet
UIWindow *window;
@property (nonatomic, retain) IBOutlet
ExemploBlogViewController *viewController;

@end

// Arquivo .m
#import "ExemploBlogAppDelegate.h"
#import "ExemploBlogViewController.h"

@implementation ExemploBlogAppDelegate

@synthesize window;
@synthesize viewController;

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:
(NSDictionary *)launchOptions {

[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];

return YES;
}

- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
@end

O arquivo .h acima define: de qual estamos herdando; (opcionalmente) quais interfaces ele implementa; as declarações de atributos; e declarações de métodos. Nesse arquivo estão definidos dois atributos: window e viewController. Em Objective-C temos o conceito de propriedades muito conhecido dos desenvolvedores Delphi e C#. Não vamos entrar em detalhes sobre isso, mas pensem nelas como sendo atalhos para métodos GET e SET.
Ainda no código acima, temos o método application:didFinishLaunchingWithOptions que é chamado quando a aplicação é carregada, e quando isso ocorre, a View (que define uma tela da aplicação) do ViewController (que trata os evento dessa tela) é adicionada a Window, e logo em seguida, torna-se visível. Ao final, o método dealloc, libera a memória alocada pela tela.
Como você pode ver, o ViewController declarado é do tipo ExemploBlogViewController (que está declarada nos arquivos de mesmo nome com extensão .h e .m). TodoViewController tem uma View associada que dispara eventos para ele. No nosso projeto, essa View está definida no arquivo ExemploBlogViewController.xib. Dê um duplo clique nesse arquivo e será aberto o InterfaceBuilder.

O InterfaceBuilder é composto por quatro janelas principais: Main, View, Attributes e Library. A primeira exibe a estrutura do arquivo; a segunda, é o editor visual onde adicionamos os componentes e podemos ter uma idéia de como ficará a tela; na janela attributes podemos alterar as propriedades dos componentes da tela; na última, temos todos os componentes que podemos adicionar à tela.


Adicione um Label da janela Library para a janela Window. Dê um duplo-clique no componente adicionado e digite um novo valor para o texto. Selecione o Label, e na janela Attributes altere a cor do texto (você consegue achar né? :). Depois altere o tamanho do texto. Por fim, salve as alterações e volte para o Xcode.

Clique no Build and Run, e o emulador do iPhone iniciará com nossa aplicação executando! \o/


Que beleza hein!? Um HelloWorld sem digitar nenhuma linha de código! :)
No próximo post vamos falar um pouco sobre Objective-C e veremos como tratar eventos de componentes.
Espero que tenham gostado. Dúvidas e melhorias, deixem seus comentários.

4br4ç05,
nglauber

sexta-feira, 4 de fevereiro de 2011

Artigo "SQLite no Android"

Olá povo,

A revista Web Mobile Magazine edição 34, trás uma matéria sobre SQLite no Android. Ela foi escrita por mim, em parceria com os meus colegas Edilson Mendes(Ronaldo!) e Breno Menezes (Cid).

Nesse artigo apresentamos, através de um exemplo completo, a persistência de dados na plataforma Android com SQLite, que traz para o mundo mobile a experiência de bancos de dados relacionais tão comuns nos ambientes web e desktop.

Espero que vocês gostem.

4br4ç05,
nglauber

domingo, 30 de janeiro de 2011

Problema ao iniciar emulador do Android

Olá povo,

Meu aluno Rogério Casal do curso de Android do Unibratec me mandou esse problema e a respectiva solução.

Ao criar um AVD (Android Virtual Device) para que possamos executar um emulador do Android, essas configurações ficam armazenadas no seguinte diretório:

Para Windows:
C:\Usuário\.android\avd\
C:\Documents and Settings\Usuário\.android\avd

Para Linux e OS X:
/home/Usuário/.android/avd/

Porém, se o nome do seu usuário contém acentuação ou cedilha, o emulador não inicia corretamente com AVDs que contenham esses caracteres. Normalmente acontece o seguinte erro:

emulator: ERROR: no search paths found in this AVD's configuration.

A solução encontrada foi mover seu arquivo de AVD para um caminho que não contenha acentuação. Para fazer isso, após criar o AVD, abra o prompt e digite o seguinte comando:

android move avd -n nome_do_avd -p C:\AVDs

O caminho especificado deve sempre criar um diretório ao invés de usar uma pasta já existente (no exemplo acima seria criado o diretório AVDs).

4br4ç05,
nglauber

segunda-feira, 24 de janeiro de 2011

Usando tecla (diálogo) de busca

Olá povo,

Ontem estava eu deitadão assitindo televisão quando chega minha digníssima esposa e me solta essa pérola: "Ei seu blog está desatualizado. A última postagem foi do começo do mês...". Sendo assim, cá estou para mais um post :)

Hoje vou mostrar pra vocês, como utilizar o botão de busca na sua aplicação, que é padrão em todos os na maioria dos aparelhos Android. Esse botão permite que você implemente a funcionalidade de busca de uma maneira padrão à do sistema operacional.

Crie no seu projeto um arquivo chamado busca.xml dentro do diretório res/xml. Nesse arquivo ficarão as configurações do diálogo de busca:


<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:hint="@string/app_hint" >
</searchable>

Notem que no arquivo acima, estamos usando duas strings que devem estar definidas no arquivo /res/values/strings.xml. Tentei usar valores hard coded e não funcionou.

Em seguida, devemos informar no AndroidManifest.xml que a atividade tratará o botão de busca. Para isso, devemos adicionar um IntentFilter com a ação android.intent.action.SEARCH e o meta-data passando nosso XML criado acima:

<activity android:name=".MinhaAtividade" >
<intent-filter>
<action
android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/busca"/>
</activity>


Feito isso, basta tratar a Intent com a busca. Coloque o código abaixo no método onCreate() da sua Activity. Caso não queira que seja aberta uma nova instância da Activity a cada nova chamada a tela de busca, marque-a no AndroidManifest.xml como android:launchMode="singleTop" e trate o evento de busca no método onNewIntent().

Intent intent = getIntent();
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
//faça sua busca com a variável query
}



Mais informações aqui.

4br4ç05,
nglauber

sexta-feira, 7 de janeiro de 2011

ANT + Antenna

Olá povo,

Esse post vai em homenagem a primeira turma de pós-graduação em Tecnologias para Desenvolvimento de Aplicações Movéis do CESAR.edu que aguentaram o mico que eu paguei quando o exemplo que eu vou mostrar nesse post não funcionou na sala. Então lá vai...

Vou demonstrar nesse post como utilizar o Ant e o Antenna. O Ant é uma ferramenta para automatização de scripts desenvolvida pela Apache Foundation. Baseia-se em tarefas a serem executadas e descritas em um arquivo XML (chamado de build file).

O Antenna é uma biblioteca que pode ser adicionada ao Ant para prover um conjunto de tarefas exclusivas para o desenvolvimento com Java ME. As tarefas realizadas pelo Antenna executam o trabalho que o WTK realiza, porém, com elas o processo de deploy (geração de um JAR/JAD) da aplicação fica mais flexível e independente de IDE.

Uma das grandes vantagens da utilização dessas ferramentas é auxiliar a prover portabilidade para a diversidade de fabricantes e modelos de aparelhos existentes no mercado, uma vez que tamanho de tela, quantidade e disponibilidade de teclas, formato de áudio e vídeo suportados são algumas das características que variam bastante de modelo para modelo.

Supondo que já temos o Eclipse configurado para Java ME, precisamos apenas baixar o JAR do Antenna que está disponível aqui. Depois, basta configurar no Eclipse o local onde você baixou o JAR. Para isso, acesso o menu Window > Preferences. No lado esquerdo, selecione Java ME, e preencha o campo Antenna JAR com o nome e local onde está o JAR do Antenna.

Vamos utilizar um projeto Java ME bem simples no Eclipse para demonstrar a utilização do Ant+Antenna. A estrutura final do projeto ficará como na imagem abaixo:


Vamos começar detalhando a classe PrincipalMIDlet.java:

public class PrincipalMIDlet extends MIDlet {

protected void startApp() {
Display.getDisplay(this).
setCurrent(new MeuCanvas());
}

protected void destroyApp(boolean uncond){
notifyDestroyed();
}

protected void pauseApp() {
}
}

class MeuCanvas extends Canvas {
//#ifdef operadora
//#expand private String OPERADORA = "%operadora%";
//#else
private String OPERADORA = "Indefinido";
//#endif

//#ifdef background
//#expand private int COR_BACKGROUND = %background%;
//#else
private int COR_BACKGROUND = 0;
//#endif

private static final int COR_TEXTO = 0xFFFFFF;

private Image icon;

public MeuCanvas() {
try {
icon = Image.createImage("/icon.png");
} catch (IOException e) {
}
}

protected void paint(Graphics g) {
int w = getWidth();
int h = getHeight();

g.setColor(COR_BACKGROUND);
g.fillRect(0, 0, w, h);

g.drawImage(icon, w/2, h/2,
Graphics.BOTTOM | Graphics.HCENTER);

g.setColor(COR_TEXTO);
g.drawString(OPERADORA, w/2, h/2,
Graphics.TOP | Graphics.HCENTER);
}
}

A parte bacana do código acima, está na definição das constantes OPERADORA e COR_BACKGROUND. Notem que elas estão envolvidas por trechos de código que aparentemente são comentários do Java, mas na verdade elas são diretivas de pré-processamento. Esse recurso é possível graças ao Antenna, que faz com que essas diretivas tenham funcionalidade similar as diretivas de compilação da linguagem C.

A instrução //#ifdef verifica se uma determinado símbolo foi definido, em caso positivo, o código-fonte que estiver dentro dessa diretiva é o que será compilado, caso contrário será compilado o código que está dentro da diretiva //#else. Toda diretiva //#ifdef deve ser fechada com a diretiva //#endif.

A diretiva //#expand substituirá qualquer símbolo que estiver entre "%" com seu respectivo valor. No nosso exemplo, definimos dois arquivos de símbolos que contêm os símbolos "operadora" e "background". Esses arquivos são mostrados abaixo:

#Claro.symbols
operadora='Claro'
background='0xFF0000'

#Vivo.symbols
operadora='Vivo'
background='0x0000FF'

Um outro detalhe a ser observado é que quando a imagem icon.png foi carregada, ela não foi carregada do diretório Vivo ou Claro. Isso porque, geraremos um JAR e um JAD da nossa aplicação para cada operadora, então os recursos de cada uma delas ficará na raiz do JAR.

Vamos agora ao script de deploy. A estrutura do build.xml, está conforme a imagem abaixo:


<?xml version="1.0" encoding="UTF-8"?>

<project name="ExemploAnt" basedir="." >

<!-- Carrega as tarefas do Antenna -->
<taskdef classpath="${antenna.lib}"
resource="antenna.properties"/>

<!-- Carrega o arquivo de propriedade -->
<property file="meubuild.properties"/>

<!-- Realiza o deploy para a Vivo -->
<target name="deployVivo">
<!-- Atribui a propriedade 'operadora' que
será utilizada no restante do script -->
<property name="operadora" value="Vivo"/>

<!-- Chama a tarefa fazerDeploy -->
<antcall target="gerarJAR" />

<!-- Chama a tarefa fazerDeploy -->
<antcall target="executarMIDlet" />
</target>

<!-- Realiza o deploy para a Claro -->
<target name="deployClaro">
<property name="operadora" value="Claro" />
<antcall target="gerarJAR" />
<antcall target="executarMIDlet" />
</target>

<!-- Gera o JAR -->
<target name="gerarJAR" depends="compilar" >
<wtkpackage
autoversion="${do-autoversion}"
bootclasspath="${wtk.libs}"
jadfile="${work.dir}/deployed/${project.name}.jad"
jarfile="${work.dir}/deployed/${project.name}.jar"
libclasspath=""
obfuscate="${do-obfuscate}"
preverify="true">

<fileset dir="${work.dir}/bin/"/>
<fileset dir="${work.dir}/resources/"/>
</wtkpackage>
</target>

<!-- Compila os arquivos *,java-->
<target name="compilar" depends="preverificar" >
<wtkbuild
bootclasspath="${wtk.libs}"
destdir="${work.dir}/bin/"
encoding="UTF-8"
preverify="false"
source="1.3"
sourcepath=""
srcdir="${work.dir}/preverified/"/>
</target>

<!-- Efetua o pré-processamento dos arquivos
*.java utilizando as diretivas -->
<target name="preverificar"
depends="copiarCodigoFonte" >

<wtkpreprocess
debuglevel="info"
destdir="${work.dir}/preverified/"
printsymbols="true"
srcdir="${work.dir}/classes"
verbose="true">

<!--Carrega o arquivo de
símbolos da operadora-->
<symbols_file name="${operadora}.symbols/"/>
</wtkpreprocess>
</target>

<!-- Copia os arquivos *.java para
o diretório de build -->
<target name="copiarCodigoFonte"
depends="copiarRecursos" >

<copy overwrite="true" todir="${work.dir}/classes/">
<fileset dir="src" includes="**/**.java"/>
</copy>
</target>

<!-- Copia os arquivos de recurso para o diretório de build -->
<target name="copiarRecursos"
depends="criarDiretorios">

<copy file="Application Descriptor"
tofile="${work.dir}/deployed/${project.name}.jad"/>

<copy todir="${work.dir}/resources/">
<fileset dir="res/${operadora}"
excludes="**/**.java"/>
</copy>
</target>

<!-- Cria os sub-diretórios do diretório de build -->
<target name="criarDiretorios" depends="limparTudo" >
<!-- Dir onde ficarão JAR e JAD -->
<mkdir dir="${work.dir}/deployed/"/>
<!-- Dir onde *.java pré-processados ficarão -->
<mkdir dir="${work.dir}/preverified/"/>
<!-- Dir onde *.java serão copiados de /src -->
<mkdir dir="${work.dir}/classes/"/>
<!-- Dir dos recursos (imagens, áudio, etc) -->
<mkdir dir="${work.dir}/resources/"/>
<!-- Dir onde os *.class serão compilados -->
<mkdir dir="${work.dir}/bin/"/>
</target>

<!-- Apaga todo o diretório de build -->
<target name="limparTudo">
<delete dir="${work.dir}" failonerror="false"/>
</target>

<!-- Executa o JAR/JAD no emulador -->
<target depends="" name="executarMIDlet">
<wtkrun
jadfile="${work.dir}/deployed/${project.name}.jad" />
</target>

</project>


Como vocês puderam notar, diversas linhas do scrpit utilizam a notação ${variavel}. Isso representa uma propriedade ou um símbolo. Os símbolos estão definidos nos arquivos *.symbols e são usados para o pré-processamento. Já as propriedades são definidas no arquivo meubuild.properties (ou individualmente como é feito com a propriedade "operadora". O arquivo meubuild.propreties ficou da seguinte forma:

# Diretório raiz do WTK
wtk.home=C\:\\WTK2.5.2_01
# Deve obfuscar?
do-obfuscate=false
# Versão do MIDP e do CLDC
wtk.midp.version=2.0
wtk.cldc.version=1.1
# MIDlet deve ser auto versionado?
do-autoversion=false
# JAR do Antenna
antenna.lib=C\:\\nglauber\\J2ME\\antenna-bin-1.2.1-beta.jar
# JARs necessários na compilação
wtk.libs=C\:\\WTK2.5.2_01\\lib\\midpapi20.jar:C\:\\WTK2.5.2_01\\lib\\cldcapi11.jar:
# Nome do projeto
project.name=ExemploAnt
# Diretório de build
work.dir=output

Para executar o script de build, arraste esse arquivo para a view Ant do Eclipse. Se não estiver visível, acesse o menu Window > Show View > Other..., na janela que for exibida escolha Ant. Feito isso, basta dar um duplo-clique sobre a tarefa deployVivo ou deployClaro.

O resultado será similar a figura abaixo:



Quem quiser mais detalhes pode dar uma olhada no artigo que escrevi pra Web Mobile 29.

4br4ç05,
nglauber