Caso prefira, você encontrará todo esse material, em inglês, no site do Developer Android. A tradução e comentários dos materiais eu faço livremente para ajudar a comunidade que fala português.

sábado, 23 de abril de 2011

4 comentários

E então, o que estão achando?

E então, o que estão achando? Programar para Android, até o momento, foi muito tranquilo, não foi? Como prometido, mostrei códigos tranquilos nesses dois primeiros exemplos e vou iniciar no próximo post a abertura do código do projeto de Notepad. Mas não será especificamente, ainda, o Note Pad que está nessa página e sim o Notepad que está nessa página.

A razão disso é que o notepad da última página citada tem, além do código em si, quatro exercícios que devemos fazer para entender uma série de aspectos da programação Android.

Assim sendo, dividirei a cobertura da programação para o notepad em quatro partes. Pelo menos eu planejo fazer assim. Caso eu perceba que o código está tranquilo e que as explicações não ficarão muito confusas, faço a análise do código em duas partes apenas.

De qualquer forma, percebemos que as peças do quebra-cabeças que é qualquer tipo de programação começa a se juntar. E então? Alguma dúvida? Alguma sugestão? Vão me dizendo que vou adequando o conteúdo às sugestões.

Até!

UPDATE

Na verdade são 3 exercícios. Acabei de ver no blog do Android.

Hello, L10N - Seus apps em inglês, francês, espanhol, etc

Nesse tutorial, vamos criar o aplicativo "Hello, L10N". (Ok, o nome não é exatamente interessante, mas é esse o nome que o tutorial do Android resolveu usar. Portanto, é o que usaremos). Essa aplicação usará o framework Android para seletivamente carregar recursos. Então nós vamos entender a localização e, baseada nela, trazer a aplicação com a lingua correta. Faremos isso adicionando arquivos de recurso no diretório res/.

Então, vamos começar? A primeira versão da aplicação L10N usará apenas as pastas padrão de recursos (res/drawable, res/layout e res/values). Esses recursos não serão internacionalizados, por assim dizer - eles serão os gráficos, layout e strings que nós esperamos que a aplicação use mais comumente. Quando o usuário rodar a aplicação a partir de uma localização padrão (i.e. Brasil ou, no caso dessa aplicação, Reino Unido) ou em locais que a aplicação não suporte (i.e. Fora do País ou, no caso dessa aplicação, fora do Reino Unido), a aplicação carregarão os recursos dessas pastas padrão.

A aplicação consiste de uma interface simples de usuário que mostra dois objetos TextView e um Button com uma imagem de fundo de uma nacionalidade. Quando clicado, o botão mostra um AlertDialog que mostra dados adicionais.

Criando a aplicação

Para essa aplicação a lingua padrão será o inglês britânico e o local padrão será o Reino Unido.

1) Inicie um novo projeto e atividade chamado HelloL10N. Insira os seguintes valores no projeto:

Project name: HelloL10N
Application name: Hello, L10N
Package name: com.example.hellol10n
Create Activity: HelloL10N
Min SDK Version: 3

O projeto básico contém uma pasta res/ com subpastas para três tipos mais comuns de recursos: graficos (res/drawable/), layouts (res/layouts/) e strings (res/values/). A maior parte da internacionalização que você fará mais tarde nesse tutorial envolverá adicionar mais subdiretórios dentro da pasta res/.

2) Abra o arquivo res/layout/main.xml e substitua o valor padrão pelo seguinte código:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:text="@string/text_a"
    />
<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:text="@string/text_b"
    />
<Button
    android:id="@+id/flag_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>
O layout linear mostrado acima têm dois objetos TextView que mostrarão dados internacionalizados e um botão que mostrará a bandeira do País.

Criando os recursos padrão

O layout se refere a recursos que precisam ser definidos.

1) Crie as strings padrão. Para fazer isso, abra o arquivo res/values/strings.xml e substitua o conteúdo do arquivo pelo que está mostrado abaixo:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Hello, L10N</string>
    <string name="text_a">Shall I compare thee to a summer"'"s day?</string>
    <string name="text_b">Thou art more lovely and more temperate.</string>
    <string name="dialog_title">No Localisation</string>
    <string name="dialog_text">This dialog box"'"s strings are not localised. For every locale, the text here will come from values/strings.xml.</string>
</resources>
O código acima provê textos em inglês britânico para cada string que a aplicação usará. Quando nos fizermos a internacionalização da aplicação, nós vamos prover textos alternativos para alemão, francês, japonês e português.

2) Adicione uma imagem para a bandeira salvando o arquivo flag.png na pasta res/drawable. Quando a aplicação não estiver internacionalizada (i.e. a aplicação entende que está no Reino Unido) será mostrada a bandeira britânica.

3) Abra o arquivo HelloL10N.java (dentro da pasta src/) e adicione o código seguinte dentro do método onCreate(), após o setContentView.

// Assinala a imagem flag.png para o botão, carregando 
// a imagem correta dependendo do local onde a pessoa está
Button b;
(b = (Button)findViewById(R.id.flag_button)).setBackgroundDrawable(this.getResources().getDrawable(R.drawable.flag));

// construa o dialog box para ser mostrado quando o usuário clicar a bandeira
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.dialog_text)
    .setCancelable(false)
    .setTitle(R.string.dialog_title)
    .setPositiveButton("Pronto", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
        dialog.dismiss();
        }
    });
//Após o AlertDialog ser construído (usando o método Builder da classe AlertDialog)
//ele é finalmente setado como sendo um AlertDialog que será mostrado.
final AlertDialog alert = builder.create();

// Insira um click listener na bandeira para mostrar o dialog box
b.setOnClickListener(
    new View.OnClickListener() 
    {
       public void onClick(View v) 
       {
          alert.show();
       }
    });

//----- Fim do código. A partir daqui, são os comentários sobre o código Java.
//----- No seu projeto, use o código acima. Não use o código abaixo.
//Perceba que na 4a linha foi criado um botão de nome b. 
//Depois o código faz um cast para que b
//referencie o botão que existe na tela, chamado flag_button. Veja que ele faz a 
//chamada para R.id.flag_button. Após isso, o código configura o background
//para usar a imagem flag.png.
//A 4a linha poderia ter sido escrita dessa maneira:

Button b;
Drawable bandeira;

b = (Button)findViewById(R.id.flag_button);
bandeira = this.getResources().getDrawable(R.drawable.flag);

b.setBackgroundDrawable(bandeira);
Dica: Se você estiver no Eclipse, uma boa dica é pressionar Control + Shift + O para encontrar e adicionar quaisquer pacotes que estejam faltando ser importados ao seu projeto.

O código acima faz o seguinte:
  • Ele assinala a bandeira correta como ícone do botão. Por enquanto, todos os recursos são definidos para o padrão. Portanto, o código sempre mostrará a bandeira britânica no botão como ícone. Uma vez que sejam adicionados mais locais e, portanto, a aplicação fique internacionalizada, o código assinalará diferentes bandeiras de acordo com o local onde a pessoa esteja.
  • Ele cria um objeto do tipo AlertDialog e seta um click listener para que quando o usuário clique no botão o AlertDialog seja mostrado. Nesse momento o AlertDialog mostrará sempre o valor de dialog_text que está localizado dentro do arquivo res/values/strings.xml.

    Rodando a aplicação sem internacionalização

    Ao rodar a aplicação, o resultado sempre será o apresentado abaixo já que a aplicação ainda não entende qual o local onde está sendo rodado e, portanto, não consegue selecionar a lingua correta.



    Planejando a internacionalização de sua aplicação

    O primeiro passo para fazer a internacionalização de sua aplicação é planejar como a aplicação vai renderiza diferentemente em diferentes locais. Nessa aplicação, o local padrão é o Reino Unido. Nós vamos adicionar informações específicas para Alemanha e França. A tabela abaixo mostra o plano para como a aplicação será mostrada em diferentes locais.

    Região/Lingua UK Alemanha França Outros locais
    Inglês Inglês britânico; bandeira britânica -- -- Inglês britânico; bandeira britânica
    Alemão -- Texto alemão para app_name, text_a e text_b, bandeira alemã -- Texto alemão para app_name, text_a e text_b, bandeira britânica
    Francês -- -- Texto francês para app_name, text_a, text_b, bandeira francesa Texto francês para app_name, text_a, text_b, bandeira britânica
    Outras linguas -- -- -- Inglês britânico, bandeira britânica

    Como mostrado na tabela acima, o plano é chamar por três ícones de bandeira em adição à bandeira inglesa que já existe na pasta res/drawable. Ele também chamará outros conjuntos de text strings para cada uma das nacionalidades cobertas pela aplicação.

    A tabela 2 mostra onde as text strings e ícones de bandeiras serão necessárias e especificam quais serão carregadas para quais locais.

    Local Lingua/País Local da strings.xml Local da flag.png
    Padrão Inglês/UK res/values/ res/drawable/
    de-rDE Alemão res/values-de/ res/drawable-de-rDE/
    fr-fFR Francês/França res/values-fr/ res/drawable-fr-rFR/

    Internacionalizando a aplicação

    Precisamos criar um arquivo string.xml para cada lingua que formos usar na aplicação além da lingua padrão. Portanto, criaremos outros três arquivos string.xml. Para tal, siga o passo a passo abaixo:

    1) Clique no ícone indicado na imagem abaixo.
    2) A tela seguinte aparecerá. Digite o nome do arquivo que se deseja criar (string.xml) e marque a opção values. Após isso, clique em Language e depois no botão que mostra uma seta para a direita.
    3) Uma vez que a seta for clicada, o qualificador será mostrado na listagem da direita. Quando a tela ficar parecida com a de baixo, digite de no campo Language.

    Pronto. O seu arquivo de localização foi criado. Repita os passos para a lingua francesa, digitando em Language as letras fr.

    A listagem de recursos deverá estar como mostrado na imagem abaixo.
     Com os arquivos string.xml criados, substitua os conteúdos de cada um deles pelos códigos apresentados abaixo:

    Alemão
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">Hallo, Lokalisierung</string>
        <string name="text_a">Soll ich dich einem Sommertag vergleichen,</string>
        <string name="text_b">Der du viel lieblicher und sanfter bist?</string>
    </resources>
    Francês
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">Bonjour, Localisation</string>
        <string name="text_a">Irai-je te comparer au jour d'été?</string>
        <string name="text_b">Tu es plus tendre et bien plus tempéré.</string>
    </resources> 

    Após isso, insira os arquivos de bandeira da Alemanha e França nas pastas respectivas dentro de res. São elas:
    • res/drawable-de-rDE para Alemanha
    • res/drawable-fr-rFR para França
    Uma vez que você adicionou a string internacionalizada e o recurso de imagem (a bandeira em si), você está pronto para rodar a aplicação e testá-la. Para testar, bastou eu mudar a lingua do smartphone para francês que a imagem abaixo foi gerada quando eu rodei a aplicação.


    E, mais abaixo, quando eu rodei a aplicação depois de mudar a lingua para alemão.


    O que aprendemos aqui é como fazer para que nossa aplicação fique internacionalizada. Como nossa lingua é a portuguesa, a única mudança aqui para que tenhamos uma aplicação com lingua padrão portuguesa é inserir as strings dentro de string.xml na lingua portuguesa e internacionalizar o restante, criando pastas para cada uma das linguas que se quer suportar.

    Lembro a vocês que essa é uma coisa que deveremos fazer SEMPRE, se quisermos realmente ganhar dinheiro desenvolvendo para Android.

    E a razão única disso é que o mercado de smartphones no Brasil ainda não é tão grande. No entanto, o mercado desse tipo de dispositivos nos EUA e Europa está bastante avançado. Então, nada de apenas pensar em ganhar dinheiro aqui no Brasil. Vamos ganhar dinheiro também fora daqui, ok? Em dolar, euro ou libra, não importa.

    sexta-feira, 22 de abril de 2011

    0 comentários

    Hello, Android!

    Eu prometi que iniciaria os exemplos a partir da tela de recursos técnicos do Android (mais precisamente com os projetos de Note Pad e Contact Manager). Mas depois, vendo direito o site, vi que existem exemplos de tutoriais.

    Então, achei melhor começar por eles. Assim a gente vai ter contato com exemplos desde os mais simples aos mais avançados, ok? De qualquer forma, o Note Pad e Contact Manager serão mostrados aqui no blog.

    O exemplo que vou pegar, o "Hello, World", pode ser encontrado nesse endereço. De qualquer forma, comentarei o exemplo na integra aqui. Esse post será em partes parecido com um que escrevi logo no início deste blog, chamado Criando um novo projeto Android. No entanto, com o conhecimento que adquirimos desde que escrevi o post citado, será MUITO mais fácil entender o código.

    Vamos começar do início. Como desenvolvedor, normalmente começamos com o exemplo clássico do Hello, World. O que precisaremos para fazer esse exemplo rodar? Abaixo vão os passos:

    1. Instalar a plataforma (Passos 01, 02, 03 e 04)
    2. Criar um AVD - Android Virtual Device.
    3. Criar o projeto Android dentro do Eclipse.

    No caso do passo 2, você poderá usar o seu smartphone para rodar o app assim que você clicar em debug. Ao invés de abrir o emulador, o Eclipse envia o app direto para o smartphone e você pode rodá-lo por lá. A vantagem é que não é preciso usar recursos do seu PC para tal tarefa. Isso será especialmente válido para apps para tablets, uma vez que a emulação do Honeycomb é bastante pesada. O caro, obviamente, é comprar um tablet. Caso queira saber como rodar o app direto no smartphone, entre nesse link que explica como debuggar.

    Depois de criar seu novo projeto Android e rodá-lo (seja no emulador ou no smartphone), vamos ver o que foi criado de código.

    Abra o arquivo HelloAndroid.java. O código deverá ser como o mostrado abaixo:


    package com.example.helloandroid;
    import android.app.Activity;
    import android.os.Bundle;
    public class HelloAndroid extends Activity {
        /* Chamado quando a atividade é criada pela primeira vez */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
        }
    }

    O que esse código nos mostra?

    A primeira linha mostra o pacote no qual a classe está inserida. Isso quer dizer que se alguém quiser acessar a classe HelloAndroid ela terá de colocar o caminho com.example.helloAndroid.HelloAndroid. Ou então, fazer um import do pacote.

    As linhas seguintes mostram os imports da classe. Isso quer dizer que são esses pacotes que serão usados nessa classe. Quando você coloca em código o import, faz com que não seja preciso, no código da classe, fazer o referenciamento completo para a classe. Se não tivessemos importado a class android.app.Activity, a classe HelloAndroid deveria ser extendida a partir de android.app.Activity e não somente Activity como mostrado acima.

    Os dois pacotes importados foram android.app.Activityandroid.os.Bundle.

    Após isso, a classe HelloAndroid é efetivamente criada. Ela será extendida a partir da classe Activity. Isso faz dela uma classe que herda as propriedades da classe Activity e, portanto, faz com que ela tenha propriedades especiais que o sistema Android interpretará para rodá-la.

    E, por fim, um único método é criado, chamado onCreate. Esse método é chamado no momento que a atividade é criada e é este recebe um Bundle com os dados da instância, caso estejamos reabrindo a aplicação após ter sido pausada (ver Ciclo de vida de uma aplicação Android). Caso seja a primeira vez que a aplicação está sendo rodada, recebe null como parâmetro.

    Uma vez que a funcionalidade padrão do método onCreate não será alterada, a primeira linha do método faz a chamada de super.onCreate(savedInstanceState). Quando chamamos super.onCreate, estamos chamando o método onCreate da classe pai (nesse caso, da classe Activity). Isso faz com que o onCreate padrão seja chamado a partir do código de implementação da classe Activity. Ponto para programação orientada a objetos. Nossa classe tem as funcionalidades de uma atividade mas não precisamos reescrever código. Basta fazer uma chamada para o método cujas funcionalidades já foram escritas e que estão presentes na classe Activity para que elas sejam rodadas a partir da classe HelloWorld.

    A linha seguinte, setContentView(R.layout.main) está especificando que o arquivo de recurso que deverá se mostrado quando a aplicação for chamada é o de nome main.xml. Caso fosse necessário fazer com que outro recurso de layout fosse a primeiro a ser carregada quando a aplicação fosse executado, bastaria mudar a chamada dentro de setContentView.

    E o que, afinal de contas, é esse R maiúsculo que vemos no código?

    O R é uma referência aos Resources (ou recursos) da sua aplicação. É um arquivo que indexa todos os recursos definidos no seu projeto. Você usa essa classe em seu código fonte como um atalho para se referir a recursos que você incluiu no projeto. É particularmente poderoso com as capacidades de code-completion de IDEs como Eclipse já que permite que você rapidamente e interativamente localize a referência especifica que está procurando.

    Abrindo o arquivo R.java que se encontra na pasta gen/ você verá todos os recursos da sua aplicação lá descritos. Perceba uma classe interna chamada "layout" e um campo membro chamado "main". O plugin ADT no Eclipse é quem criou essa seção. Ele "percebeu" a existência do arquivo main.xml e gerou uma classe para ele. Se você adicionar outros recursos ao seu projeto (como strings dentro de res/values/strings.xml ou imagens dentro de res/drawable) o seu arquivo R.java será mudado para refletir essas mudanças.

    /* AUTO-GENERATED FILE.  DO NOT MODIFY.
     *
     * This class was automatically generated by the
     * aapt tool from the resource data it found.  It
     * should not be modified by hand.
     */
    package com.example.helloandroid;
    public final class R {
        public static final class attr {
        }
        public static final class drawable {
            public static final int icon=0x7f020000;
        }
        public static final class id {
            public static final int textview=0x7f050000;
        }
        public static final class layout {
            public static final int main=0x7f030000;
        }
        public static final class string {
            public static final int app_name=0x7f040001;
            public static final int hello=0x7f040000;
        }
    }


    Se você não estiver usando o Eclipse, essa classe será gerada para você quando você estiver compilando o projeto.

    Nota: Você nunca deverá editar esse arquivo manualmente. Deixe que o Eclipse, através do plugin ADT, faça as modificações para você.

    Se por um lado os recursos devem estar relacionados no arquivo R.java, as atividades devem todas estar descritas no arquivo de manifesto:
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.helloandroid"
          android:versionCode="1"
          android:versionName="1.0">
     
        <uses-sdk android:minSdkVersion="7" /> 
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".HelloAndroid"
                      android:label="@string/app_name">

                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
     
    </manifest>

    Eu fiz a separação em cores para poder explicar melhor o arquivo. Na parte cinza, lá em cima, existe a declaração do arquivo XML. Essa declaração é padrão da XML e não deverá ser modificada.

    Na segunda parte, em vermelho, temos o início da chave manifest, que traz alguns dados importantes a respeito da aplicação. Vejam que existe alguns atributos, sendo eles:

    • package - Mostra qual é o pacote da aplicação. Isso é que mostrará para o sistema Android qual é a identificação da aplicação.
    • android:versionCode - Mostra a versão do código. Atualmente, na versão 1.
    • android:versionName - Mostra a versão do código em formato mais, digamos, didático ao usuário. Atualmente na versão 1.0.

    Em azul, vemos a seção <uses-sdk> que tem apenas um atributo: android:minSdkVersion indicando o número 7. Isso diz respeito à versão da API que será a versão mínima na qual o app vai conseguir rodar. Nesse caso, a API 7 representa a versão do Android SDK 2.1 update 1. A tabela de SDK e valores de API você encontra aqui.

    Em verde temos a abertura da seção de <application>, que trará as configurações a respeito da aplicação em si.

    Em roxo, temos a seção <activity>, que descreve a única atividade existente na aplicação. No caso, a atividade existente é a HelloAndroid. 

    E por fim, em laranja, temos a seção <intent-filter>. Nela temos algumas entradas que descrevem a ação e a categoria da atividade. O intent-filter é responsável por comunicar ao sistema Android a intenção (intent, em inglês) da aplicação e de uma determinada atividade interna a ela. É uma palavra que pode confundir já que dá a entender que, por ter uma intenção, deverá ser executada com tal finalidade. Na verdade, ela tem uma intenção em si mas só será executada se e quando solicitada. 

    Explico: veja que existe a tag action dentro de intent-filter. O atributo interno a essa tag é o android:name e que está marcado como android.intent.action.MAIN. Além dela, existe a tag category que tem como android:name a inscrição android.intent.category.LAUNCHER.

    A primeira linha diz que essa atividade será a principal e que, além de inflar o recurso de layout main.xml na tela, deverá rodar essa atividade inicialmente.

    Já a segunda linha, a de category, especifica que a aplicação deverá ser mostrada no launcher do Android e que, portanto, poderá ser acessada bastando tocar no ícone dele na tela do smartphone.

    Quando a aplicação roda, a tela ao lado é mostrada:

    O que aconteceu? A aplicação subiu e inflou o recurso de layout e executou a atividade que estava marcada como MAIN diretamente na tela.

    E se quisessemos não chamar a view padrão e definir algum conteúdo a ser inflado na tela inicialmente de maneira programática?

    Bastaria, para tal, modificar a classe para o código que você vê abaixo:


    package com.example.helloandroid;
    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.TextView;
    public class HelloAndroid extends Activity {
       /** Chamado quando a atividade é criada pela primeira vez */
       @Override
       public void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
    
           TextView tv = new TextView(this);
           tv.setText("Esse é um TextView carregado direto na tela a partir do método OnCreate");
           setContentView(tv);
    
       }
    }
    O que mudou no código acima do anterior? Fizemos um novo import de android.widget.TextView e dentro do método onCreate, dimensionamos uma variável do tipo TextView, inserimos um valor dentro dele e colocamos essa TextView como sendo o conteúdo a ser mostrado.

    O resultado é o seguinte:


    Obviamente, como vimos no post que fala sobre Declaração de Layouts, não vale a pena inserir um TextView diretamente na tela. Afinal, o que faremos apenas com esse widget na tela? Absolutamente nada. O ideal é inserir um TextView dentro de algum tipo de layout já que declarar o layout programaticamente é possível, como visto, mas faz da manutenção do código um inferno rapidamente.

    E como fazer a declaração deste TextView diretamente em um arquivo de layout e fazer com que o aplicativo o chamasse e eu não precisasse fazer a declaração via código do meu layout? É simples e é que vamos fazer abaixo.

    Dentro do Eclipse, expanda a árvore de diretórios até achar a /res/layout/. Dentro dele haverá um arquivo chamado main.xml (nome sugestivo, não? principal.xml na tradução para o português).


    Substitua o conteúdo do arquivo pelo conteúdo abaixo:
    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/textview"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:text="@string/hello"/>
    Dentro da pasta /res/values/ abra o arquivo strings.xml. Aqui é onde você deverá guardar todos os textos padrões da sua interface de usuário. E qual a razão para se fazer isso? Imagine que você queira fazer um aplicativo que seja voltado para o público que fala português. Dai você cria o aplicativo e vai inserindo os textos que deverão aparecer nos widgets diretamente no corpo do XML responsável pelo layout. Ou seja, no lugar de @string/hello, como aparece acima, você coloca diretamente o texto que deseja que apareça. O problema dessa abordagem é que se um dia você quiser fazer com que seu aplicativo possa ser usado por um público que fala inglês, francês ou qualquer outra lingua, você terá de percorrer todos os arquivos de layout fazendo a modificação diretamente no XML ao passo que usando um arquivo como o strings.xml você mantem todas as mensagens em um local apenas.

    Uma vez aberto o arquivo strings.xml, você verá o seguinte conteúdo:
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="hello">Hello World, HelloAndroid!</string>
        <string name="app_name">Hello, Android</string>
    </resources>
    Modifique a string hello para a mensagem que deseja. Eu mudarei para "Olá mundo, eu fui preenchido com uma string de recurso!". Veja que quando o código android:text é setado para @string/hello, o que o aplicativo fará é substituir essa entrada pela mensagem correspondente existente em strings.xml.

    Por fim, faça com que o código da classe HelloAndroid.java fique como era inicialmente:

    package com.example.helloandroid;
    import android.app.Activity;
    import android.os.Bundle;
    public class HelloAndroid extends Activity {
        /* Chamado quando a atividade é criada pela primeira vez */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
        }
    }

    Ao rodar a aplicação, você terá o seguinte resultado:


    E esse foi o primeiro exemplo de código comentado aqui no blog CeleiroAndroid. Nos próximos posts, mais código e mais entendimento de como as peças são encaixadas para se criar um "motor" de aplicativo Android. Até mais!

    terça-feira, 19 de abril de 2011

    2 comentários

    Novo Layout

    Pessoal, a versão beta do novo layout está pronta e no ar. Ainda está faltando apenas a imagem de cabeçalho, que terminarei de noite. No entanto, achei que o layout estava bem estável para que eu pudesse colocá-lo no ar. Espero que gostem.

    Alguns itens passaram a não funcionar e vou ver o que posso fazer. São eles a pesquisa do Google interna ao blog e também o contador de hits.

    Se puderem me dar um feedback a respeito do layout, se gostaram, o que pode mudar, etc, será de grande valor.

    Qual exemplo da página de Recursos Técnicos do Android usar?

    Estava dando uma olhada no site de recursos técnicos e vi alguns excelentes exemplos. Só que ainda não havia decidido sobre qual abrir. Eu queria um que fosse simples mas que não fosse simplório demais. No entanto, pensando que pode haver pessoas que estão acessando esse blog e que ainda não estão inteirados com programação, seria algo ruim eu colocar um exemplo de dificuldade média ou avançada (eu mesmo ficaria meio perdido, já que também estou aprendendo com vocês a programar em Android).

    Então, eu decidi fazer um review de dois códigos: Note Pad e Contact Manager. O primeiro é bem simples e mostra uma aplicação para se salvar notas no Android. Já o Contact Manager mostra como fazer uma pesquisa pelos contatos existentes no seu sistema usando a API de ContactsContract assim como inserir contatos em uma conta específica.

    Entendo que assim estarei cobrindo alguns assuntos interessantes (interface e acesso a bancos de dados) e uma maioria ficará satisfeito.

    Eu pensei também em analisar o código do jogo Snake (aquele da cobra que fica correndo a tela buscando comer os insetos e frutas). Se der tempo faço isso também.

    O caso é que vou esperar o feriadão para poder abrir os códigos e comentá-los por aqui, ok? Então, fiquem a postos e, caso não viajem, acessem o blog para poder checar como é o código e minhas análises.

    segunda-feira, 18 de abril de 2011

    1 comentários

    Fazendo o Debugging de seus Apps no Smartphone - USB/Wi-Fi

    O leitor Rogério Rodrigues (@rbrodrigues) fez uma excelente pergunta outro dia: como fazer o debugging de seus Apps no Smartphone. Bem, ao invés de responder apenas a ele, resolvi criar um post para que pudesse falar disso para todos.

    Fiz uma pesquisa e vi que existem duas maneiras de se fazer o debug direto no smartphone. A primeira é a convencional, via cabo USB ligado ao PC onde o Eclipse está rodando. E a segunda, que achei muito bacana, é via Wi-Fi. Isso mesmo que você leu: Wi-Fi! O único senão é que seu smartphone terá de estar em modo root para que essa opção funcione.

    Quando você escreve uma aplicação móvel é importante sempre testar a aplicação em um dispositivo real antes de lançá-lo no mercado. Isso acontece pois nem sempre a emulação é 100% fiel ao que acontece no dispositivo.

    Você pode usar qualquer dispositivo Android como o seu ambiente para realizar debug e testar suas aplicações. As ferramentas incluídas no SDK fazem dessa tarefa algo fácil. Então, instalar e rodar suas aplicações a cada vez que precisar fazer o debug é tranquilo. Você poderá fazer a instalação de sua aplicação diretamente no Eclipse ou a partir de linha de comando com o ADB.

    E o que é ADB?

    Android Debug Bridge é uma ferramenta versátil que permite que você gerencie o estado de uma instância de emulação de um dispositivo Android. Dentro da nossa necessidade, será a interface que permitirá o Eclipse se comunicar com o dispositivo para que possa haver o debug no aparelho e não no PC.

    Fazendo o debugging via cabo USB

    Com um dispositivo Android, você terá de seguir os passos abaixo:

    1) Declare sua aplicação como "debugável" no seu manifesto do Android de sua aplicação.

    a) Para tal, abra o arquivo AndroidManifest.xml. Ele deverá se encontra no mesmo local que o mostrado abaixo:

    b) Selecione a guia Application para que você vá para a tela onde fará a alteração.

    c) Por fim, selecione o menu dropdown Debuggable e modifique para True.

    2) Modifique a configuração USB Debugging do seu dispositivo. No dispositivo, vá até a tela HOME, pressione MENU e selecione Aplicativos > Desenvolvimento e depois certifique-se que a opção Depuração USB está marcada. Se houver a opção USB debugging notify, deixe-a marcada também.


    3) Será necessário que você instale um driver USB para o ADB. Caso você tenha um ADP (Android Developer Phone) ou um Nexus One ou Nexus S, procure o driver nesse site. Caso tenha um outro aparelho, a partir da marca dele, pesquise os Drivers OEM USB aqui. No caso de você ter baixado o sofware oficial de gerenciamento de conteúdo do seu smartphone, é bem provável que os drivers OEM USB já estejam instalados.

    4) Por fim, navegue via Prompt do DOS até a pasta onde o SDK do Android foi instalado (normalmente ele estará em c:\Program Files\Android\android-sdk\platform-tools\) e execute o comando adb devices. Se tudo tiver corrido bem, uma mensagem como a de baixo aparecerá mostrando que o sistema operacional reconhece que existe um dispositivo Android conectado a ele via USB.

    5) Certo, mas e como devo fazer o debug? Simples. Com o seu projeto aberto, clique no botão mostrado na imagem abaixo e escolha Run Configurations.

     Uma janela se abrirá. Nela é que está o pulo do gato. Escolha o projeto na guia Android como mostrado na imagem abaixo e depois vá para a guia target.

    Na guia target escolha a opção Manual ao invés de Automatic.
    Por fim, já que o seu dispositivo está conectado ao PC, basta escolhê-lo e clicar em OK para que o APK seja enviado para ele e seja rodado. Caso existam pontos de debug, o Android enviará de volta para o ADB que está conectado ao Eclipse e você poderá ver o que está acontecendo por trás. Percebam que a imagem diz que eu deveria escolher um dispositivo compatível com o Android 3.0. É que quando criei esse projeto, eu o fiz para mostrar o emulador do Android Honeycomb 3.0. Mesmo assim, ele funcionará no meu Froyo 2.2.1.

    Olha só a tela do HelloAndroid rodando no meu Xperia X10 e ele também presente no meu Launcher.



    Fazendo o Debugging de seus Apps no Smartphone - Wi-Fi

    Basicamente o processo é o mesmo. A única diferença é que ao invés de conectarmos ao smartphone via cabo USB, o faremos via Wi-Fi. Para isso, é necessário que seu smartphone esteja em modo root.  Estar em modo root é fazer com que as aplicações que rodam no smartphone possam elevar seu nível de acesso para su (super user). Infelizmente esse tópico de como fazer o seu aparelho ficar em modo root está além do escopo do post, dado que existem inúmeros aparelhos de diversos fabricantes diferentes no mercado.

    Quando conseguir fazer com que o aparelho esteja nesse modo root, instale um software a partir do Android Market chamado adbWireless. Basicamente ele permite que se faça um acesso para o aparelho via Wi-Fi, como se fosse via cabo USB.

    Desplugue o cabo USB do seu smartphone.

    Uma vez instalado o software, ele trará uma tela com o endereço de acesso ao aparelho, como poderá ver na imagem abaixo:


    Abra novamente o prompt do DOS com direitos de Administrador (caso você esteja no Windows 7 ou Vista) e vá até a pasta do SDK do Android (normalmente instalado em c:\Program Files\Android\android-sdk\platform-tools\).

    Execute os seguintes comandos:
    • abd devices
      Provavelmente nenhum dispositivo estará sendo mostrado, dado que você desplugou o cabo USB do PC.
    • adb connect 192.168.0.105:5555
      O adb tentará achar o dispositivo no endereço e porta fornecidos. Esse endereço é relativo ao meu aparelho e você deverá inserir o endereço que aparecer no seu adbWireless.
    • adb devices
      Agora será mostrado o dispositivo, que está conectado via Wi-Fi.

    Agora basta repertir os passos a partir do item 5 de como debuggar usando o cabo USB. A única diferença será quando chegar à tela abaixo:

    Perceba que ao invés de mostrar o ID do meu aparelho mostrará o endereço IP:Porta que eu usei para me conectar junto ao ADB. Bacana, não? 

    Pronto, comece logo a debuggar seus apps agora via USB ou Wi-Fi.

    Em que pé estamos no Android?

    Bem, essas últimas semanas foram de muitos conteúdos relativamente chatos. Afinal, foram apenas de teoria, teoria, teoria... Cobri uma série de aspectos: toda a parte de Interface bem como Services, Content Providers, Intents e Intent Filters além de Processes e Threads.

    Mas, como fazer para juntar isso tudo de maneira a realmente conseguir criar um app do zero? O que vou fazer é pegar um projeto do site do Android na área de recursos técnicos e abrí-lo por aqui e mostrar como cada parte funciona.

    Assim vamos começar a entender melhor como é que as peças do Android se encaixam e tudo roda tranquilamente. O melhor é que o código foi criado pela equipe da Google responsável pelo Android. Então é um código enxuto e, principalmente, dentro das conformidades necessárias para que rode sem maiores problemas nos emuladores/aparelhos.

    Aliás, é isso também que vou falar no próximo post: como debuggar diretamente no seu aparelho.

    Até lá!

    domingo, 17 de abril de 2011

    0 comentários

    Processes e Threads

    Quando um componente de aplicação inicia e a aplicação não tem outros componentes rodando, o sistema Android inicia outro processo Linux para a aplicação com uma single thread de execução. Por padrão, todos os componentes da mesma aplicação rodam em um mesmo processo e thread (chamada thread principal). Se um componente de aplicação inicia e existe um processo para aquela aplicação (pois outro componente da aplicação já existe), então o componente é iniciado dentro do processo e usa a mesma thread de execução. Contudo, você pode fazer com que diferentes componentes em sua aplicação rodem em processos separados e poderá também criar threads adicionais para quaisquer processos.

    Processos

    Por padrão, todos os componentes da mesma aplicação rodam em um mesmo processo e a maioria das aplicação não devem mudar isso. Mas, se você precisa de controle sobre quais processos um certo componente pertence, então você deve fazê-lo dentro do arquivo de manifesto da aplicação.

    A entrada no manifesto para cada tipo de elemento componente da aplicação - <activity>, <service>, <receiver> e <provider> - suportam um atributo android:process que pode especificar o processo no qual o componente deverá rodar. Você pode configurar esse atributo para que cada componente rode em seu próprio processo e para que alguns componentes compartilhem processos enquanto outros não. Você pode também configurar android:process para que componentes de diferentes aplicações rodem em um mesmo processos - desde que as aplicações comportilhem o mesmo ID de usuário Linux e sejam assinados com os mesmos certificados.

    O elemento <application> também suporta o atributo android:process para que se possa configurar um valor padrão que se aplica a todos os componentes.

    O Android deverá decidir a matar algum processo em algum ponto, como quando a memória está baixa ou é requerida por outro processo que seja mais importante naquele momento. Componentes de aplicação rodando em um processo que está sendo morto são, consequentemente, mortos também. Um processo é iniciado novamente para esses componentes quando eles forem novamente requisitados.

    Quando o Android está decidindo sobre qual processo matar, ele pesa sua importância relativa ao usuário. Por exemplo, é mais fácil o Android matar processos que hospedam atividades que não estão mais visíveis ao usuário que outros que estão visíveis. A decisão sobre terminar um processo ou não, portanto, depende do estado do componente rodando naquele processos.

    Ciclo de vida do processo

    O sistema Android tenta manter um processo de aplicação pelo maior tempo possível carregado mas eventualmente ele necessita remover processos antigos para liberar memória para novos e mais importantes processos. Para determinar que processos manter e quais matar o sistema colocar a cada processo um status de importância dentro de uma hierarquia baseada nos componentes que estão sendo rodados no processo e o estado de seus componentes. Processos com importância menor são eliminados primeiro e então aqueles subsequentes até o mais importante.

    Existem cinco níveis de importância na hierarquia. A lista seguinte mostra os diferentes tipos de processos em ordem de importância (sendo o primeiro o mais importante e que será morto por último dentro da hierarquia).
    • Foreground Process (processos que rodam em aplicações que estão na tela do usuário)
      Um processo que é requerido para o que o usuário está fazendo naquele momento. Um processo é considerado em modo foreground se alguma das condições abaixo forem verdadeiras:
      • O processo hospeda uma Activity na qual o usuário está interagindo (quando o método onResume() da atividade foi chamado);
      • O processo hospeda um Service que está ligado a atividade na qual o usuário está interagindo;
      • O processo hospeda um Service que está rodando em modo foreground - o serviço chamou startForeground();
      • O processo hospeda um Service que está executando alguns dos callbacks do ciclo de vida (onCreate(), onStart(), onDestroy());
      • O processo hospeda um BroadcastReceiver() que está executando o método onReceive().
      Geralmente apenas poucos processos em modo foreground existem em qualquer momento. Eles são mortos apenas como último recursos - no caso da memória estar tão baixa que não pode sequer continuar rodando.
    • Processo visível
      Um processo que não tem componentes em modo foreground mas que ainda assim afetam o que o usuário vê na tela. Um processo é considerado visível se uma das sequintes condições forem verdadeiras:
      • O processo hospeda uma Activity que não está em foreground mas continua visível para o usuário (seu método onPause() foi chamado). Isso pode ocorrer, por exemplo, se uma atividade em modo foreground chamou um diálogo que permite que a atividade anterior possa ser vista por ele (diálogo flutuante e transparente, por exemplo).
      • O processo hospeda um Service que está ligado a uma atividade visível em modo foreground.
      Um processo visível é considerado extremamente importante e não será morto a não ser que retirá-lo da memória seja parte do que a atividade que está rodando necessita (i.e. o processo foi morto de maneira declarada via código).
    • Processo de serviço
      Um processo que esteja rodando um serviço que foi inicializado com o método startService() e não está dentro das duas categorias anteriores. Apesar desses serviços não estarem diretamente ligados ao que o usuário está fazendo no momento, eles são geralmente relacionados com atividades que o usuário está desempenhando, como ouvir música ou fazendo o download de dados da rede. Então o sistema mantém esse processo ativo a não ser que necessite da memória usada por ele.
    • Serviços em Background
      Um processo que hospeda uma atividade que não está mais visível ao usuário (o método onStop() foi chamado). Esses processos não tem impacto direto na experiência do usuário e podem ser mortos pelo sistema para liberar memória.
    • Processos vazios
    • Um processo que não hospeda qualquer atividade que esteja ativa. A única razão de se manter esse processo ativo é por razões de caching, para que ele seja carregado mais rapidamente quando o usuário precisar dele.
    O Android faz um ranking dos processos no nível mais alto que eles possam estar, baseado na importância dos componentes ativos no momento.

    Em adição a isso, o ranking de um determinado processo será aumentado se outros processos dependem dele.

    Threads

    Quando uma aplicação é lançada o sistema cria uma thread de execução para aquela aplicação, chamada de thread principal. Essa thread é muito importante pois ela é encarregada de fazer o gerenciamento dos processos subsequentes. Ela também é chamada de UI Thread ou Thread de Interface.

    O sistema NÃO cria uma thread separada para cada instância de um componente. Todos os componentes que rodam em um mesmo processo são instanciados na mesma UI Thread e as chamadas a cada componente que o sistema faz são despachadas a partir dessa thread. Consequentemente, métodos que respondem a chamadas callback do sistema (como onKeyDown()) sempre rodam na UI Thread do processo.

    Por conta disso, quando um usuário toca um botão na tela, a UI Thread de sua aplicação envia um evento touch para o widget que então modifica seu estado para pressionado e posta uma requisição dizendo que é inválido. A UI Thread retira da fila a requisição e notifica o widget que ele tem de ser redesenhado (é quando vemos a imagem do botão como pressionado).

    Quando sua aplicação realiza trabalho intensivo em resposta a uma interação do usuário, uma thread simples pode começar a ter uma resposta pobre a não ser que você faça implementações em sua aplicação. Especificamente, se tudo está rodando na UI Thread, realizar longas tarefas como um acesso a rede ou banco de dados por bloquear toda a interface da sua aplicação enquanto essa tarefa estiver rodando. Pior, a aplicação pode demorar tanto a responder que o sistema Android poderá entender que ela está travada e mostrar a mensagem de "application not responding". O usuário então terá de decidir se sair da aplicação ou se a desinstala.

    Worker thread

    Com o modelo de thread única mostrada acima, é vital que a responsividade de sua interface de aplicação não bloqueie a UI Thread. Se as operações que são realizadas não são instantâneas então você deve se certificar de criá-las em threads separadas (threads backgroud ou worker threads).

    Por exemplo, abaixo está um código de um listener click que faz o download de uma imagem em uma thread separada e a mostra dentro de um ImageView:
    public void onClick(View v) {
        new Thread(new Runnable() {
            public void run() {
                Bitmap b = loadImageFromNetwork("http://example.com/image.png");
                mImageView.setImageBitmap(b);
            }
        }).start();
    }

    Inicialmente, parece ser um código perfeito, já que ele cria uma nova thread e manuseia a operação de rede. No entanto, ele viola a segunda regra do modelo de thread única: não acessar a toolkit de interface do Android de fora da UI Thread. Esse exemplo modifica um ImageView a partir de um Worker Thread ao invés da UI Thread.

    Para corrigir esse problema, o Android oferece diversas maneiras de acessar a UI Thread de outras threads. Aqui a lista de alguns métodos que podem ajudá-lo nessa tarefa:
    • Activity.runOnUiThread(Runnable)
    • View.post(Runnable)
    • View.postDelayed(Runnable, long)
    Por exemplo, você poderá corrigir o código acima usando o método View.post(Runnable):
    public void onClick(View v) {
        new Thread(new Runnable() {
            public void run() {
                final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
                mImageView.post(new Runnable() {
                    public void run() {
                        mImageView.setImageBitmap(bitmap);
                    }
                });
            }
        }).start();
    }

    Agora sua implementação está em modo thread-safe: a operação de rede é realizada em uma thread separada enquanto a ImageView é manipulada pela UI Thread.

    Só que com isso a complexidade de sua operação aumenta e esse código pode ficar complicado de manter. Para diminuir tal dificuldade em interações mais complexas com a worker thread, você deve considerar usar um Handler em seu worker thread para processar mensagens entregues à UI Thread. Talves a melhor solução, no entanto, seja extender a classe AsyncTask que simplifica a execução das tarefas worker thread que precisam interagir com a interface.

    Usando AsyncTask

    AsyncTask permite a você realizar trabalhos assincronos na sua interface de usuário. Ele realiza operações de bloqueio em worker threads e então publica seu resultado na UI Thread sem requerer que você manuseie threads ou handlers.

    Para usá-lo, você deve criar uma subclass de AsyncTask e implementar o método doInBackground() que roda num pool de threads de background. Para atualizar sua interface, você deve implementar um onPostExecute() que entrega o resultado de doInBackground() e roda dentro da UI Thread para que você possa atualizar de maneira segura a interface. Você pode, então, roda essa tarefa chamando execute() direto da thread.
    public void onClick(View v) {
        new DownloadImageTask().execute("http://example.com/image.png");
    }
    private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
        /** The system calls this to perform work in a worker thread and
          * delivers it the parameters given to AsyncTask.execute() */
        protected Bitmap doInBackground(String... urls) {
            return loadImageFromNetwork(urls[0]);
        }
      
        /** The system calls this to perform work in the UI thread and delivers
          * the result from doInBackground() */
        protected void onPostExecute(Bitmap result) {
            mImageView.setImageBitmap(result);
        }
    }

    Veja como o código agora está mais seguro e mais fácil de manter, já que separa o trabalho.

    Você deveria ler o material de AsyncTask para uma referência completa e para entender completamente o uso dessa classe, mas aqui vai um overview de como ela funciona:
    • Você pode especificar o tipo de parâmetro, os valores de progresso e o valor final da tarefa, usando generics.
    • O método doInBackground() é executado automaticamente na worker thread.
    • onPreExecute(), onPostExecute() e onProgressUpdate() são chamados na UI Thread.
    • O valor retornado pelo doInBackground() é enviado para onPostExecute()
    • Você pode chamar publishProgress a qualquer momento em doInBackground() para executar onProgressUpdate() na UI Thread.
    • Você pode cancelar a tarefa a qualquer momento a partir de qualquer thread.
    Métodos Thread-Safe

    Em algumas situações, os métodos que você implementa devem ser chamados de mais de uma thread e, portanto, devem ser escritas como thread-safe.

    Isso é primariamente verdadeiro para métodos que podem ser chamados remotamente - como métodos em um bound service. Quando uma chamada a um método implementado em um IBinder é originado em um mesmo processo no qual o IBinder está rodando, o método é executado na thread de quem o chama. No entanto, quando a chamada se origina em outro processo, o método é executado em uma thread escolhida de um pool de threads que o sistema mantém no mesmo processo do IBinder. Por exemplo, se um método onBind() fosse chamado a partir da UI Thread os métodos implementados em onBind() retornados seriam chamados em uma thread escolhida a partir do pool de threads. Como o serviço pode ter mais de um cliente, mais de um thread da pool pode tentar executar o mesmo método IBinder. Por essa razão, métodos IBinder devem ser implementados como sendo thread-safe.

    Similarmente, um content provider pode receber dados requisitados que originam-se em outros prcessos. Apesar das classes ContentResolver e ContentProvider esconder detalhes de como os processos de comunicação interprocessos são gerenciados, os métodos que respondem a essas requisições - query(), insert(), delete(), update() e getType() - são chamados de um pool de threads dentro dos processos do content provider e não dos processos da UI Thread. Como esses métodos podem ser chamados de diversas threads a cada vez, elas devem ser implementadas como sendo thread-safe.

    Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.
    Related Posts Plugin for WordPress, Blogger...