You are on page 1of 29

Começando a desenvolver aplicativos para Android

23 de março de 2010 1 comentário

Este post é o primeiro de uma série em que vou ensinar, passo a passo, como criar um
aplicativo para a plataforma android.

Android é a plataforma do google para dispositivos móveis que equipa um grande número de
telefones no mercado. (g1, motorola dext, milestone, nexus one…)

O que preciso para começar a desenvolver para android?

Uma idéia e força de vontade. E claro, saber programar em Java. Você NÃO precisa de um
hardware (telefone) para isso. A grande maioria dos testes pode ser feito no emulador!

Além disso, Android é uma plataforma de código aberto e o desenvolvimento de programas é


amplamente incentivado pelo Google (e pela Motorola, como vamos ver no final do post).

Por onde começar?

O primeiro passo é montar seu ambiente de desenvolvimento.

1) Montar o ambiente padrão fornecido pelo Google. Para isso, você precisará seguir os
seguintes passos:

- Instalar o Eclipse (www.eclipse.org)


- Instalar o Android SDK (developer.android.com/sdk)
- Instalar o ADT Plugin (developer.android.com/sdk/eclipse-adt.html)

Todos os links contém as instruções para instalação dos componentes. Caso haja dúvidas,
coloque nos comentários!

DICA: Você pode “economizar” os passos acima usando o ambiente do Motodev – que é
basicamente a junção de todos os passos acima e mais algumas ferramentas. Para instalar o
Motodev Studio vá até a página http://developer.motorola.com/docstools/motodevstudio/

É importante dizer que os aplicativos gerados pelo Motodev Studio funcionarão em todos os
telefones, e não só em telefones Motorola.
Criando um projeto Android (Helloworld!)
No artigo da semana passada vimos como montar o ambiente de desenvolvimento android.
Caso seu ambiente ainda não esteja funcionando, volte lá e veja o que faltou.

Hoje iremos criar nosso primeiro projeto android – o nosso “Helloworld”.

Passo 1 – Criando o projeto no Eclipse

Abra o Eclipse, vá até File>New>Project

Na tela que aparecer, escolha “Android Project” e clique em “Next”.

Criando um "Android Project"

Após isso, irá aparecer a tela com as configurações de seu projeto android.

Nesta tela, você precisa inserir os seguintes dados:

• Project name - É o nome do projeto no eclipse.


• Build Target – É a versão do Android para a qual o seu projeto será direcionado.
• Application name – É o nome da sua aplicação – o nome que aparecerá no telefone.
• Package name - É o package no qual serão criadas as suas classes java.
• Create Activity – Marque este checkbox e coloque um nome na caixa de texto. À
frente explicarei o que é uma Activity.

Depois disso, basta clicar em “Finish”.


Configurando o projeto android

Passo 2 – Imprimindo um texto

Após isso, será criado um novo projeto e dentro dele,


na pasta src/<nome_do_package>/ você encontrará um
arquivo .java com o nome da Activity que você colocou
no passo anterior.

Para fazer a sua aplicação imprimir um texto na tela,


modifique este arquivo dessa forma:

view plaincopy to clipboardprint?

1. package br.com.felipesilveira.hello_world;
2.
3. import android.app.Activity;
4. import android.os.Bundle;
5. import android.widget.TextView;
6.
7. public class HelloWorld extends Activity {
8. /** Called when the activity is first created. */
9. @Override
10. public void onCreate(Bundle savedInstanceState) {
11. super.onCreate(savedInstanceState);
12. TextView view = new TextView(this);
13. view.setText("Hello, Android");
14. setContentView(view);
15.
16. }
17. }

Parte 3 – Rodando a aplicação no emulador

Para rodar nosso recém criado programa no emulador do google, vá até “Run”>Run as
“Android Application”. Uma instância do emulador será criada, com o nosso “HelloWorld”
rodando.

O que é uma Activity?

Neste HelloWorld tivemos contato com o primeiro elemento de um código android: A


Activity.

Uma Activity é basicamente uma classe gerenciadora de UI (Interface com o usuário). Todo
aplicativo android começa por uma Activity. Para saber mais, veja a documentação da classe
Activity. Nos próximos artigos falaremos bastante sobre ela, suas características, seu ciclo de
vida e como manipulá-la corretamente.

DICA: Além de rodar a aplicação, você pode explorar um pouco o emulador, para conhecer
o sistema operacional Android, caso ainda não conheça. Durante o desenvolvimento, o
emulador será seu melhor amigo, então essa é a oportunidade para conhecê-lo bem.

Trabalhando com layouts XML em Android


Olá pessoal, hoje vou falar sobre a definição do layout de sua aplicação Android usando
XML.

Definir os layouts através dessa técnica é a forma mais comum de se começar o


desenvolvimento de uma aplicação.

Para exemplificar os estudos que faremos a partir de agora, usaremos uma aplicação de
exemplo, que construiremos juntos, a partir dos próximos posts.

A idéia é fazer uma aplicação onde o usuário poderá entrar com algumas anotações, e
visualizar as últimas entradas.

O layout será mais ou menos assim:

Criando o main.xml

Para novos projetos android, o arquivo main.xml já é


automaticamente criado. Ele fica no diretório res/layout, com
o conteúdo:

view plaincopy to clipboardprint?

1. <?xml version="1.0" encoding="utf-8"?>


2. <LinearLayout xmlns:android="http://schemas.androi
d.com/apk/res/android"
3. android:orientation="vertical"
4. android:layout_width="fill_parent"
5. android:layout_height="fill_parent"
6. >
7. <TextView
8. android:layout_width="fill_parent"
9. android:layout_height="wrap_content"
10. android:text="@string/hello"
11. />
12. </LinearLayout>

Neste arquivo temos contato com os primeiros elementos de um arquivo de layout XML:
• LinearLayout, que é apenas um container.
• TextView, que é um elemento de texto. Nesse caso está imprimindo a string cujo id é
@string/hello. (Não se preocupe, falaremos sobre strings e seus ids à frente nesse
curso)

Para criar um layout parecido com o rascunho do início do post, iremos inserir outros dois
elementos:

• EditText – uma caixa de texto onde o usuário irá entrar com as anotações;
• ListView – uma lista de anotações previamente submetidas.

Assim, o nosso novo XML:

view plaincopy to clipboardprint?

1. <?xml version="1.0" encoding="utf-8"?>


2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:orientation="vertical"
4. android:layout_width="fill_parent"
5. android:layout_height="fill_parent"
6. >
7. <EditText
8. android:id="@+id/edit_box"
9. android:layout_width="fill_parent"
10. android:layout_height="wrap_content"
11. android:text="Nova nota..."
12. >
13. </EditText>
14. <ListView
15. android:id="@+id/notes_list"
16. android:layout_width="fill_parent"
17. android:layout_height="wrap_content"
18. >
19. </ListView>
20. </LinearLayout>

Carregando o arquivo XML na aplicação

Para que a nossa aplicação tenha o layout definido pelo arquivo XML, é preciso carregá-lo.

Isso é feito através da função setContentView(), como no código abaixo:

view plaincopy to clipboardprint?

1. public void onCreate(Bundle savedInstanceState) {


2. super.onCreate(savedInstanceState);
3. setContentView(R.layout.main);
4. }
O parâmetro R.layout.main indica que o arquivo de layout a ser carregado é o main.xml. (Se
o se arquivo se chamar abobrinha.xml, o parâmetro deverá ser R.layout.abobrinha)

É possível utilizar mais de um arquivo XML para uma mesma tela, para formar layouts mais
sofisticados. Trataremos disso à frente nesse curso.

Compilando o nosso projeto e rodando no emulador, temos o seguinte resultado:

QuickNotes rodando no emulador

No próximo post iremos acrescentar um botão a este layout, e iremos aprender um pouco
mais sobre os parâmetros de um documento XML.

DICA: Existe uma ferramenta online, gratuita, para edição de arquivos de layout XML. É o
DroidDraw.

LEITURA RECOMENDADA: Para aprender mais sobre a definição de layout de aplicações


android, visite a página “User Interface” da documentação oficial. (em inglês)
Adicionando um botão a um layout android

Para adicionar um botão clicável ao nosso layout, usaremos o widget Button.

Queremos que ele fique ao lado esquerdo da caixa de texto. Como fazer isso?

1. Diminuir o tamanho da caixa de texto. Para fazer isso, é só alterar a propriedade


android:layout_width. Originalmente o valor dela era “fill_parent” – isso quer dizer:
“ocupe todo o espaço disponível”. Ao invés disso, vamos usar 240 pixels.
2. Inserir o botão logo após a caixa de texto.
3. Inserir um novo LinearLayout que irá conter a caixa de texto e o botão. Isso é
necessário porque o LinearLayout que definimos anteriormente (e que ocupa toda a
tela) tem a propriedade android:orientation=”vertical”. Essa propriedade faz com
que seus elementos sejam dispostos verticalmente (um debaixo do outro) e não é isso
que queremos para estes dois elementos (a caixa de texto e o botão – queremos que
fiquem lado a lado).

Assim, temos o nosso novo main.xml:

view plaincopy to clipboardprint?

1. <?xml version="1.0" encoding="utf-8"?>


2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:layout_width="fill_parent"
4. android:layout_height="fill_parent"
5. android:orientation="vertical"
6. >
7. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
8. android:layout_width="fill_parent"
9. android:layout_height="fill_parent"
10. >
11. <EditText
12. android:id="@+id/edit_box"
13. android:layout_width="240px"
14. android:layout_height="wrap_content"
15. android:text="Nova nota..."
16. >
17. </EditText>
18. <Button
19. android:id="@+id/insert_button"
20. android:layout_width="80px"
21. android:layout_height="wrap_content"
22. android:text="Inserir"
23. >
24. </Button>
25. </LinearLayout>
26. <ListView
27. android:id="@+id/notes_list"
28. android:layout_width="fill_parent"
29. android:layout_height="wrap_content"
30. >
31. </ListView>
32. </LinearLayout>

Compilando as alterações, temos a seguinte tela:

Novo layout da aplicação android - agora com o botão "Inserir"

No próximo post veremos como controlar o botão de “inserir”.


Activity – o que é isso?
Hoje iremos conhecer uma das mais importantes classes de uma aplicação Android: A classe
Activity.

No post “Criando um projeto Android (Helloworld!)” comecei a falar sobre ela:

Uma Activity é basicamente uma classe gerenciadora de UI (Interface com o usuário). Todo
aplicativo android começa por uma Activity.

Ou seja, quando uma aplicação android é executada, na verdade é a sua Activity principal que
é lançada.

Ciclo de vida de uma Activity

Uma das coisas que é importante conhecer sobre a Activity é o seu ciclo de vida. E para
explicá-lo, nada melhor do que o seguinte diagrama*:
Ciclo de vida de uma Activity

Este diagrama é de fundamental importância para o correto entendimento do funcionamento


de uma aplicação android. Ele introduz, implicitamente, os estados que uma Activity pode
estar, os quais explico no desenho abaixo:
Estados de uma Activity

Voltando ao diagrama do ciclo de vida, temos as seguintes funções:

• onCreate() É a primeira função a ser executada quando uma Activity é lançada.


Geralmente é a responsável por carregar os layouts XML e outras operações de
inicialização. É executada somente uma vez durante a “vida útil” da Activity.

• onStart() É chamada imediatamente após a onCreate() – e também quando uma


Activity que estava em background volta a ter foco.
• onResume() Assim como a onStart(), é chamada na inicialização da Activity (logo
após a própria onStart()) e também quando uma Activity volta a ter foco. Qual a
diferença entre as duas? A onStart() só é chamada quando a Activity não estava mais
visível na tela e volta a ter o foco, enquanto a onResume() sempre é chamada nas
“retomadas de foco”.
• onPause() É a primeira função a ser invocada quando a Activity perde o foco (ou
seja, uma outra Activity vem à frente).

• onStop() – Análoga à onStart(), só é chamada quando a Activity fica completamente


encoberta por outra Activity (não é mais visível).

• onDestroy() A última função a ser executada. Depois dela, a Activity é considerada


“morta” – ou seja, nao pode mais ser relançada. Se o usuário voltar a requisitar essa
Activity, outro objeto será contruído.
• onRestart() Chamada imediatamente antes da onStart(), quando uma Activity volta a
ter o foco depois de estar em background.

Executando uma Activity

Já sabemos que quando a sua aplicação é executada, a Activity definida como padrão (na
criação do projeto) é lançada. Mas eu posso criar outras Activities?

É claro que sim.


E para executar outras Activities, basta usar as funções startActivity() e
startActivityForResult(). No exemplo abaixo, lançamos uma segunda Activity a partir da
principal, e esperamos um resultado dela – como se fosse um retorno de função.

view plaincopy to clipboardprint?

1. static final int PICK_CONTACT_REQUEST = 0;


2.
3. @Override
4. public void onCreate(Bundle savedInstanceState) {
5. super.onCreate(savedInstanceState);
6. setContentView(R.layout.main);
7. startActivityForResult(
8. new Intent(Intent.ACTION_CONTACT_REQUEST,
9. new Uri("content://contacts")),
10. CONTACT_REQUEST);
11. }
12.
13. protected void onActivityResult(int requestCode, int resultCode,
14. Intent data) {
15. if (requestCode == CONTACT_REQUEST) {
16. if (resultCode == RESULT_OK) {
17. // fazer alguma coisa...
18. }
19. }

Quando a segunda Activity terminar a sua execução, a função onActivityResult() será


invocada, com o “resultado” como parâmetro.

Mas como uma Activity define o seu resultado, a ser lido por aquela que a chamou?

Isso é feito invocando-se a função setResult (int resultCode), como por exemplo:

1. setResult(Intent.RESULT_OK);

Alguém percebeu que eu não disse nada sobre os parâmetros da startActivityForResult()?


Isso é porque este é o assunto do meu próximo post – o mecanismo de Uris em Android. Até
lá!

Criando uma Activity secundária


6 de maio de 2010 5 comentários

No post passado vimos como lançar uma Activity a partir de outra, usando as funções
startActivity() e startActivityForResult().

Hoje usaremos esta técnica para mostrar ao usuário uma tela de “Boas Vindas” na nossa
aplicação de exemplo, o QuickNotes.
Para criar essa nova Activity, usaremos alguma funções do Motodev. Se você não está
usando a IDE da Motorola, não tem problema – é só criar os arquivos manualmente. Porém
recomendo o uso da IDE, por facilitar bastante a nossa vida.

Vamos começar criando a Activity que dará “Boas Vindas” ao usuário.

Vá até o menu “MOTODEV” >”New” > “New Android Activity”. Na tela de configuração,
entre com o nome da Activity a ser criada:

Configurando a Activity a ser criada

Após clicar em “Finish”, já haverá a classe “WelcomeActivity” no diretório src do nosso


projeto.

Com a Activity criada, o próximo passo é criar o arquivo XML que definirá o seu layout.
Crie o arquivo ‘welcome.xml’ no diretorio res/layout com o seguinte conteúdo:

view plaincopy to clipboardprint?

1. <?xml version="1.0" encoding="utf-8"?>


2. <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:layout_width="fill_parent"
4. android:layout_height="fill_parent"
5. >
6. <TextView android:id="@+id/welcome_text_view"
7. android:layout_width="fill_parent"
8. android:layout_height="300dip"
9. android:gravity="center"
10. android:text="Bem vindo à aplicação QuickNotes!\n\nEssa aplicação foi feita durante
o curso
11. 'Desenvolvendo para Android' do site felipesilveira.com.br"
12. >
13. </TextView>
14. <Button
15. android:id="@+id/welcome_ok_button"
16. android:layout_width="fill_parent"
17. android:layout_height="wrap_content"
18. android:text="Continuar"
19. >
20. </Button>
21. </TableLayout>

Este arquivo XML define uma Activity com um texto e um botão logo abaixo, com a palavra
“Continuar”.

Após criado o arquivo, vamos carregá-lo no método onCreate() da WelcomeActivity():

1. setContentView(R.layout.welcome);

Lançando a WelcomeActivity

Para lançar a WelcomeActivity a partir da MainActivity, usaremos a função startActivity().


Esta função recebe como parâmetro um Intent. Posteriormente iremos aprofundar nosso
conhecimento sobre essa importante classe, mas por enquanto o que você precisa saber é que
ela é usada para fazer a comunicação entre Activities.

No código abaixo instanciamos um Intent cuja única função é lançar a WelcomeActivity, e


então o usamos como parâmetro para a startActivity.

Dessa forma, o código da MainActivity fica assim:

view plaincopy to clipboardprint?

1. package br.com.felipesilveira.quicknotes;
2.
3. import android.app.Activity;
4. import android.os.Bundle;
5. import android.content.Intent;
6.
7. public class MainActivity extends Activity {
8. /** Called when the activity is first created. */
9. @Override
10. public void onCreate(Bundle savedInstanceState) {
11. super.onCreate(savedInstanceState);
12. setContentView(R.layout.main);
13.
14. Intent i = new Intent(this, WelcomeActivity.class);
15. startActivity(i);
16. }
17. }
Tratando os eventos de um botão

Até agora, já temos a Activity secundária sendo lançada, mas o que deve acontecer quando o
usuário clicar no botão “Continuar”?

A WelcomeActivity deve morrer - Dessa forma, a última Activity instanciada será mostrada
novamente – que por sinal é a nossa MainAcitivity!

Para fazer isso, devemos adicionar um listener ao botão para que o método finish() seja
invocado ao clique do usuário. O método finish() da classe Activity força a “morte” desta.

O código da WelcomeActivity fica assim:

view plaincopy to clipboardprint?

1. package br.com.felipesilveira.quicknotes;
2.
3. import android.app.Activity;
4. import android.os.Bundle;
5. import android.view.View;
6. import android.widget.Button;
7.
8. public class WelcomeActivity extends Activity {
9. /**
10. * @see android.app.Activity#onCreate(Bundle)
11. */
12. @Override
13. protected void onCreate(Bundle savedInstanceState) {
14. super.onCreate(savedInstanceState);
15. setContentView(R.layout.welcome);
16.
17. final Button button = (Button) findViewById(R.id.welcome_ok_button);
18.
19. button.setOnClickListener(new View.OnClickListener() {
20. public void onClick(View v) {
21. finish();
22. }
23. });
24. }
25. }

Executando nosso projeto, temos a seguinte tela:


WelcomeActivity sendo executada

Trabalhando com logs em android


13 de maio de 2010 Sem comentários

Hoje irei falar sobre um mecanismo simples porém muito útil para uma aplicação android: os
logs.

Os logs permitem ao desenvolvedor debugar erros durante o desenvolvimento e também


investigar problemas com o software em produção, ou seja, com o usuário final.

Para este fim, android tem um classe específica: A classe Log (android.util.Log).

Para criar os log, temos à disposição as funções Log.v(), Log.d(), Log.i(), Log.w(), r Log.e().

Mas por que tantas funções?

Porque os log em java tem alguns tipos – ou níveis – são eles:

• DEBUG – logs impressos pela função Log.d()


• ERROR – logs impressos pela função Log.e()
• INFO – logs impressos pela função Log.i()
• VERBOSE – logs impressos pela função Log.v()
• WARN – logs impressos pela função Log.w()

Todas estas funções recebem como parâmetros duas strings – a primeira, chamada de TAG, e
a segunda que é a mensagem em si. A TAG é uma string que irá identificar a sua aplicação,
tornando mais fácil identificar quais logs foram impressos por ela. (Todas as aplicações
imprimem o log no mesmo stream. Assim, a única forma de separar os seus logs é filtrando
pela sua Tag)

É uma boa prática definir a TAG como uma string constante:

view plaincopy to clipboardprint?

1. private static final String TAG = "QuickNotesMainActivity";

Dessa forma, para imprimir um log de DEBUG basta usar a linha abaixo:

view plaincopy to clipboardprint?

1. Log.d(TAG, "mensagem de debug");

Visualizando logs pelo DDMS

Para visualizar os logs de nossa aplicação, usaremos o DDMS. Abrindo a aba “Logcat”,
temos a seguinte tela:

Lendo logs pelo logcat


No ponto 1 marcado na imagem, temos os botões de “filtragem de níveis”. Estes botões
permitem que você escolha quais logs quer ver – DEBUG, por exemplo. Já no ponto 3, você
pode escrever um texto, que será um filtro para os logs. Por exemplo, se digitar
“QuickNotesMainActivity”, irá ver só os logs que nós colocamos no código acima.

Finalmente, no ponto 2 temos a mensagem de log em si.

DICA: Seja cuidadoso com os logs. Eles podem interferir na performance de sua aplicação
se, por exemplo, forem colocados dentro de um loop.

Content Providers
Os Content Providers são parte importantíssima da arquitetura de um sistema android. É
responsabilidade deles prover às aplicações o conteúdo que elas precisam para funcionar, ou
seja, os dados.

Mas por que são realmente necessários?

As aplicações poderiam muito bem acessar diretamente um banco de dados, por exemplo.
Porém, é uma boa prática tornar o modo como os dados são gravados transparente à
aplicação. Dessa forma, a aplicação pode manter o foco nas interações com o usuário.

Além disso, essa técnica permite a criação de Shared Content Providers, que são providers
“públicos” que podem ser acessados por várias aplicações. Por exemplo, existe o content
provider de SMS/MMS que permite a qualquer aplicação ler as mensagens recebidas por um
telefone celular.

E como é feita a comunicação entre Content Providers e Aplicações?

Uri. Guarde bem este nome, pois você irá precisar muito dele durante a sua carreira como
desenvolvedor android.

Toda a comunicação entre aplicações e providers é feita através dos métodos da interface
ContentProvider, que sempre recebem um objeto Uri como parâmetro. O formato da Uri
é definido pelo content provider. Por exemplo, a Uri content://sms/inbox acessa as mensagens
de inbox no Content Provider de SMS. Falaremos um pouco mais sobre as Uris a seguir, mas
primeiro, vamos conhecer os métodos que usaremos para enviá-las para o provider:

• query(Uri, String[], String, String[], String)- usado para recuperar


dados.
• insert(Uri, ContentValues) – usado para inserir dados.
• update(Uri, ContentValues, String, String[]) – usado para atualizar dados.
• delete(Uri, String, String[]) – usado para deletar dados.
• getType(Uri) – usado para obter o MIME type de certo dado.
O QuickNotes Content Provider

Depois dessa rápida introdução, vamos colocar a mão na massa.

Iremos criar um content provider para o QuickNotes, que servirá para gravar e recuperar as
anotações do usuário, da seguinte forma:

Intencionalmente coloquei a caixa que define


como o provider irá gravar os dados para mostrar que isso é irrelevante para a aplicação.

A estrutura das URIs

Uma Uri usada para acessar Content Provider segue o formato:

content://<authority>/<parametro1>/<parametro2>/…/<parametroN>

Onde authority é o “nome” do provider, e os parâmetros são aqueles definidos pelo provider.
Por exemplo, a seguinte Uri:

content://sms/conversations/10

Acessa o Content Provider de SMS, e seleciona a conversation de Id número 10.

Criando um Content Provider

Para criar seu próprio content provider, é preciso fazer 2 coisas:

1. Criar uma sub-classe da ContentProvider, implementando os métodos públicos que eu


citei no começo do artigo;
2. Registrar o provider no AndroidManifest.xml

Vamos começar criando a classe QuickNotesProvider:

view plaincopy to clipboardprint?

1. package br.com.felipesilveira.quicknotes;
2.
3. import android.content.ContentProvider;
4. import android.net.Uri;
5. import android.content.ContentValues;
6. import android.database.Cursor;
7.
8. public class QuickNotesProvider extends ContentProvider {
9. // Aqui definimos os formatos possíveis de Uri que
10. // o nosso provider irá aceitar.
11. public static final Uri CONTENT_URI = Uri
12. .parse("content://br.com.felipesilveira.quicknotes.quicknotesprovider");
13.
14. @Override
15. public int delete(Uri uri, String selection, String[] selectionArgs) {
16. return 0;
17. }
18.
19. @Override
20. public String getType(Uri uri) {
21. return null;
22. }
23.
24. @Override
25. public Uri insert(Uri uri, ContentValues values) {
26. return null;
27. }
28.
29. @Override
30. public boolean onCreate() {
31. return false;
32. }
33.
34. @Override
35. public Cursor query(Uri uri, String[] projection, String selection,
36. String[] selectionArgs, String sortOrder) {
37. return null;
38. }
39.
40. @Override
41. public int update(Uri uri, ContentValues values, String selection,
42. String[] selectionArgs) {
43. return 0;
44. }
45. }

Agora, vamos registrar o nosso provider no AndroidManifest, adicionando a seguinte linha


entre as tags <application …> e </application>

<provider
android:authorities="br.com.felipesilveira.quicknotes.quicknotesprovider"
android:name=".QuickNotesProvider"/>

E assim o nosso Content Provider está pronto para receber requisições da aplicação. Ainda
não retorna nenhum resultado significativo – mas isso faremos no próximo artigo, onde
ensinarei como acessar um banco de dados SQLite, para fazer esse provider realmente
efetivo.

DICA: Usando o MOTODEV Studio, a tarefa de criar um content provider fica muito mais
fácil. Basta acessar New > Android Content Provider, que um template será criado, com
todos os métodos! daí é só implementar a lógica deles.

Como usar banco de dados em uma aplicação android


Um dos grandes diferenciais da plataforma android é a grande quantidade de módulos e APIs
que as aplicações tem à disposição para usar. Eles dão muito poder ao desenvolvedores,
permitindo que estes façam coisas que eram impossíveis em outras plataformas móveis.

Um dos mais importantes módulos é o SQLite. Sim, amigos, já temos um SGDB (Sistema
gerenciador de bancos de dados) instalado e pronto para usar! E é exatamente o que faremos
no artigo de hoje.

No artigo anterior vimos como criar um Content Provider. Usaremos este provider para
acessar o banco de dados.

Para fazer isso, precisamos implementar os métodos da classe ContentProvider que vimos no
artigo passado (query(), delete(), update(), etc…) para prover ao usuário os métodos para
criar, atualizar, deletar e recuperar os dados. Além disso, usaremos a classe
SQLiteOpenHelper para gerenciar a conexão com o banco de dados.

A classe SQLiteOpenHelper

A classe SQLiteOpenHelper, como dito anteriormente, será usada para gerenciar o banco de
dados. Para usá-la, é preciso criar uma subclasse implementando os métodos abaixo:

• onCreate() – Este método é chamado quando a conexão com o banco de dados for
aberta pela primeira vez. É aqui que criaremos o banco de dados, com o comando sql
CREATE.
• onUpdate() – Este método é chamado quando a versão do banco de dados muda. Por
exemplo, digamos que você criou uma nova versão de seu aplicativo que usa uma
tabela a mais no banco de dados. Quando esta nova versão for instalada (em um
telefone que já possuir a primeira versão) este método será chamado, então você
poderá criar apenas a nova tabela, mantendo os dados do usuário.

O código

O código do QuickNotesProvider fica assim, acessando o banco de dados. A seguir, eu


explico algumas coisas que podem gerar dúvidas.

view plaincopy to clipboardprint?

1. package br.com.felipesilveira.quicknotes;
2.
3. import java.util.HashMap;
4.
5. import android.content.ContentProvider;
6. import android.content.ContentUris;
7. import android.content.Context;
8. import android.content.UriMatcher;
9. import android.net.Uri;
10. import android.provider.BaseColumns;
11. import android.content.ContentValues;
12. import android.database.Cursor;
13. import android.database.sqlite.SQLiteDatabase;
14. import android.database.sqlite.SQLiteOpenHelper;
15. import android.database.sqlite.SQLiteQueryBuilder;
16.
17. public class QuickNotesProvider extends ContentProvider {
18.
19. // Authority do nosso provider, a ser usado nas Uris.
20. public static final String AUTHORITY =
21. "br.com.felipesilveira.quicknotes.quicknotesprovider";
22.
23. // Nome do arquivo que irá conter o banco de dados.
24. private static final String DATABASE_NAME = "quicknotes.db";
25.
26. // Versao do banco de dados.
27. // Este valor é importante pois é usado em futuros updates do DB.
28. private static final int DATABASE_VERSION = 1;
29.
30. // Nome da tabela que irá conter as anotações.
31. private static final String NOTES_TABLE = "notes";
32.
33. // 'Id' da Uri referente às notas do usuário.
34. private static final int NOTES = 1;
35.
36. // Tag usada para imprimir os logs.
37. public static final String TAG = "QuickNotesProvider";
38.
39. // Instância da classe utilitária
40. private DBHelper mHelper;
41.
42. // Uri matcher - usado para extrair informações das Uris
43. private static final UriMatcher mMatcher;
44.
45. private static HashMap<string, string=""> mProjection;
46.
47. static {
48. mProjection = new HashMap<string, string="">();
49. mProjection.put(Notes.NOTE_ID, Notes.NOTE_ID);
50. mProjection.put(Notes.TEXT, Notes.TEXT);
51. }
52.
53. static {
54. mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
55. mMatcher.addURI(AUTHORITY, NOTES_TABLE, NOTES);
56. }
57.
58. /////////////////////////////////////////////////////////////////
59. // Métodos overrided de ContentProvider //
60. /////////////////////////////////////////////////////////////////
61. @Override
62. public int delete(Uri uri, String selection, String[] selectionArgs) {
63. SQLiteDatabase db = mHelper.getWritableDatabase();
64. int count;
65. switch (mMatcher.match(uri)) {
66. case NOTES:
67. count = db.delete(NOTES_TABLE, selection, selectionArgs);
68. break;
69. default:
70. throw new IllegalArgumentException(
71. "URI desconhecida " + uri);
72. }
73.
74. getContext().getContentResolver().notifyChange(uri, null);
75. return count;
76. }
77.
78. @Override
79. public String getType(Uri uri) {
80. switch (mMatcher.match(uri)) {
81. case NOTES:
82. return Notes.CONTENT_TYPE;
83. default:
84. throw new IllegalArgumentException(
85. "URI desconhecida " + uri);
86. }
87. }
88.
89. @Override
90. public Uri insert(Uri uri, ContentValues values) {
91. switch (mMatcher.match(uri)) {
92. case NOTES:
93. SQLiteDatabase db = mHelper.getWritableDatabase();
94. long rowId = db.insert(NOTES_TABLE, Notes.TEXT, values);
95. if (rowId > 0) {
96. Uri noteUri = ContentUris.withAppendedId(
97. Notes.CONTENT_URI, rowId);
98. getContext().getContentResolver().notifyChange(
99. noteUri, null);
100. return noteUri;
101. }
102. default:
103. throw new IllegalArgumentException(
104. "URI desconhecida " + uri);
105. }
106. }
107.
108. @Override
109. public boolean onCreate() {
110. mHelper = new DBHelper(getContext());;
111. return true;
112. }
113.
114. @Override
115. public Cursor query(Uri uri, String[] projection, String selection,
116. String[] selectionArgs, String sortOrder) {
117. // Aqui usaremos o SQLiteQueryBuilder para construir
118. // a query que será feito ao DB, retornando um cursor
119. // que enviaremos à aplicação.
120. SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
121. SQLiteDatabase database = mHelper.getReadableDatabase();
122. Cursor cursor;
123. switch (mMatcher.match(uri)) {
124. case NOTES:
125. // O Builer receberá dois parametros: a tabela
126. // onde será feita a busca, e uma projection -
127. // que nada mais é que uma HashMap com os campos
128. // que queremos recuperar do banco de dados.
129. builder.setTables(NOTES_TABLE);
130. builder.setProjectionMap(mProjection);
131. break;
132.
133. default:
134. throw new IllegalArgumentException(
135. "URI desconhecida " + uri);
136. }
137.
138. cursor = builder.query(database, projection, selection,
139. selectionArgs, null, null, sortOrder);
140.
141. cursor.setNotificationUri(getContext().getContentResolver(), uri);
142. return cursor;
143. }
144.
145. @Override
146. public int update(Uri uri, ContentValues values, String selection,
147. String[] selectionArgs) {
148. SQLiteDatabase db = mHelper.getWritableDatabase();
149. int count;
150. switch (mMatcher.match(uri)) {
151. case NOTES:
152. count = db.update(NOTES_TABLE, values,
153. selection, selectionArgs);
154. break;
155. default:
156. throw new IllegalArgumentException(
157. "URI desconhecida " + uri);
158. }
159.
160. getContext().getContentResolver().notifyChange(uri, null);
161. return count;
162. }
163.
164. /////////////////////////////////////////////////////////////////
165. // Inner Classes utilitárias //
166. /////////////////////////////////////////////////////////////////
167. public static final class Notes implements BaseColumns {
168. public static final Uri CONTENT_URI = Uri.parse("content://"
169. + QuickNotesProvider.AUTHORITY + "/notes");
170.
171. public static final String CONTENT_TYPE =
172. "vnd.android.cursor.dir/" + QuickNotesProvider.AUTHORITY;
173.
174. public static final String NOTE_ID = "_id";
175.
176. public static final String TEXT = "text";
177. }
178.
179. private static class DBHelper extends SQLiteOpenHelper {
180.
181. DBHelper(Context context) {
182. super(context, DATABASE_NAME, null, DATABASE_VERSION);
183. }
184.
185. /* O método onCreate é chamado quando o provider é executado pela
186. * primeira vez, e usado para criar as tabelas no database
187. */
188. @Override
189. public void onCreate(SQLiteDatabase db) {
190. db.execSQL("CREATE TABLE " + NOTES_TABLE + " (" +
191. Notes.NOTE_ID + " INTEGER PRIMARY KEY AUTOINCRE
MENT," +
192. Notes.TEXT + " LONGTEXT" + ");");
193. }
194.
195. /* O método onUpdate é invocado quando a versão do banco de dados
196. * muda. Assim, é usado para fazer adequações para a aplicação
197. * funcionar corretamente.
198. */
199. @Override
200. public void onUpgrade(SQLiteDatabase db,
201. int oldVersion, int newVersion) {
202. // Como ainda estamos na primeira versão do DB,
203. // não precisamos nos preocupar com o update agora.
204. }
205. }
206. }
207. </string,></string,>

Cursores

O primeiro conceito importante a se falar é o conceito dos Cursores. Como você deve
percebido, este é o tipo de retorno do método query(), e não é por acaso: Os cursores são
“apontadores de dados” do banco de dados – ou seja, uma interface que permite o acesso aos
dados retornados pela query enviada pelo usuário.

notifyChanges()

Em todos os métodos em que alteramos o banco de dados (inserimos, deletamos ou


modificamos dados) é importante chamar o método modifyChanges(). Isso fará com que as
aplicações que estejam utilizando este conjunto de dados sejam notificadas, permitindo a
estas atualizar também os dados mostrados ao usuário.

Acessando um Content Provider


No artigo de hoje começaremos a integrar a nossa aplicação QuickNotes com o
QuickNotesProvider, que criamos no artigo anterior.

Vamos começar inserindo uma anotação do usuário no banco de dados. Para fazer isso, o
primeiro passo é adicionar um Listener ao botão ‘Inserir’, da seguinte forma:

1. Button insertButton = (Button)findViewById(R.id.insert_button);


2. insertButton.setOnClickListener(mInsertListener);

E agora, criando o objeto mInsertListener. Ele precisa ser um objeto que implementa a
interface OnClickListener,. Assim, precisamos implementar o método onClick(), que será
chamado assim que o usuário pressionar o botão.

view plaincopy to clipboardprint?

1. // Definindo um OnClickListener para o botão "Inserir"


2. private OnClickListener mInsertListener = new OnClickListener() {
3. public void onClick(View v) {
4. EditText editBox = (EditText)findViewById(R.id.edit_box);
5. addNote(editBox.getText().toString());
6. editBox.setText("");
7. }
8. };

No código acima eu fiz uma chamada a um método que ainda não está implementado – o
método addNote(), que recebe um String que será inserida no banco de dados. Ele será o
método responsável por efetivamente “conversar” com o content provider. Vamos
implementá-lo:
view plaincopy to clipboardprint?

1. /*
2. * Método responsável por inserir um registro no content provider
3. */
4. protected void addNote(String text) {
5. ContentValues values = new ContentValues();
6. values.put(QuickNotesProvider.Notes.TEXT, text);
7.
8. getContentResolver().insert(
9. QuickNotesProvider.Notes.CONTENT_URI, values);
10. }

Assim, a MainActivity fica dessa forma:

view plaincopy to clipboardprint?

1. package br.com.felipesilveira.quicknotes;
2.
3. import android.app.Activity;
4. import android.os.Bundle;
5. import android.view.View;
6. import android.view.View.OnClickListener;
7. import android.widget.Button;
8. import android.widget.EditText;
9. import android.content.ContentValues;
10. import android.content.Intent;
11.
12. public class MainActivity extends Activity {
13.
14. private static final String TAG = "QuickNotesMainActivity";
15.
16. /** Called when the activity is first created. */
17. @Override
18. public void onCreate(Bundle savedInstanceState) {
19. super.onCreate(savedInstanceState);
20. setContentView(R.layout.main);
21.
22. Intent i = new Intent(this, WelcomeActivity.class);
23. startActivity(i);
24.
25. Button insertButton = (Button)findViewById(R.id.insert_button);
26. insertButton.setOnClickListener(mInsertListener);
27.
28. // adicionando um 'Hint' ao Editbox.
29. EditText editBox = (EditText)findViewById(R.id.edit_box);
30. editBox.setHint("Nova nota...");
31. }
32.
33. // Definindo um OnClickListener para o botão "Inserir"
34. private OnClickListener mInsertListener = new OnClickListener() {
35. public void onClick(View v) {
36. EditText editBox = (EditText)findViewById(R.id.edit_box);
37. addNote(editBox.getText().toString());
38. editBox.setText("");
39. }
40. };
41.
42. /*
43. * Método responsável por inserir um registro no content provider
44. */
45. protected void addNote(String text) {
46. ContentValues values = new ContentValues();
47. values.put(QuickNotesProvider.Notes.TEXT, text);
48.
49. getContentResolver().insert(
50. QuickNotesProvider.Notes.CONTENT_URI, values);
51. }
52. }

É importante salientar que estamos apenas inserindo o dado no banco de dados – não estamos
lendo-o em nenhum lugar na nossa aplicação, ainda.

Mas como saber se o dado realmente foi inserido no banco de dados?

Vou aproveitar esta pergunta para mostrar como acessar o banco de dados pelo shell do
android. Isso é muito útil para auxiliar no desenvolvimento de aplicações que lidam com
banco de dados.

Acessando banco de dados através do shell

Para acessar o banco de dados pelo shell, iremos iniciar uma sessão com o comando adb
shell e então usar o comando sqlite3 <caminho-do-db>. A partir daí, basta usar comandos
SQL normais. Para ver a estrutura do banco de dados, o comando .schema deve ser usado.
Veja no exemplo abaixo:

$ adb shell
# sqlite3 /data/data/br.com.felipesilveira.quicknotes/databases/quicknotes.db
SQLite version 3.5.9
Enter “.help” for instructions
sqlite> .schema
.schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE notes (_id INTEGER PRIMARY KEY AUTOINCREMENT,text
LONGTEXT);
sqlite> select * from notes;
select * from notes;
1|teste
Como podemos ver, a primeira entrada do nosso DB está lá, então é sinal que o
QuickNotesProvider está funcionando corretamente!

No próximo artigo, iremos usar o provider para ler os dados já gravados no DB para mostrá-
los ao usuário. Até lá!

You might also like