-
Este post o primeiro de uma srie em que vou ensinar, passo a
passo, como criar um
aplicativo para a plataforma android.
Android a plataforma do google para dispositivos mveis que
equipa um grande
nmero de telefones no mercado. (g1, motorola dext, milestone,
nexus one)
O que preciso para comear a desenvolver para android?
Uma idia e fora de vontade :). E claro, saber programar em Java.
Voc NO precisa
de um hardware (telefone) para isso. A grande maioria dos testes
pode ser feito no
emulador!
Alm disso, Android uma plataforma de cdigo aberto e o
desenvolvimento de
programas amplamente incentivado pelo Google (e pela Motorola,
como vamos ver no
final do post).
Por onde comear?
O primeiro passo montar seu ambiente de desenvolvimento.
1) Montar o ambiente padro 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 contm as instrues para instalao dos componentes.
Caso haja
dvidas, coloque nos comentrios!
DICA: Voc pode economizar os passos acima usando o ambiente do
Motodev que basicamente a juno de todos os passos acima e mais
algumas ferramentas. Para
instalar o Motodev Studio v at a pgina
http://developer.motorola.com/docstools/motodevstudio/
importante dizer que os aplicativos gerados pelo Motodev Studio
funcionaro em
todos os telefones, e no s em telefones Motorola.
-
Com isso terminamos o nosso primeiro passo. No prximo passo, vou
mostrar como
criar um projeto android no eclipse. At l!
-
Criando um projeto Android (Helloworld!)
2 de abril de 2010 100 comentrios
No artigo da semana passada vimos como montar o ambiente de
desenvolvimento
android. Caso seu ambiente ainda no 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"
Aps isso, ir aparecer a tela com as configuraes de seu projeto
android.
Nesta tela, voc precisa inserir os seguintes dados:
Project name - o nome do projeto no eclipse.
Build Target a verso do Android para a qual o seu projeto ser
direcionado. Application name o nome da sua aplicao o nome que
aparecer no
telefone.
Package name - o package no qual sero 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
Aps isso, ser criado um novo projeto e dentro dele, na pasta
src//
voc encontrar um arquivo .java com o nome da Activity que voc
colocou no passo
anterior.
Para fazer a sua aplicao 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 aplicao no emulador
-
Para rodar nosso recm criado programa no emulador do google, v
at Run>Run as Android Application. Uma instncia do emulador ser
criada, com o nosso HelloWorld rodando.
Hello World rodando no emulador
O que uma Activity?
Neste HelloWorld tivemos contato com o primeiro elemento de um
cdigo android: A
Activity.
Uma Activity basicamente uma classe gerenciadora de UI
(Interface com o usurio).
Todo aplicativo android comea por uma Activity. Para saber mais,
veja a
documentao da classe Activity. Nos prximos artigos falaremos
bastante sobre ela,
suas caractersticas, seu ciclo de vida e como manipul-la
corretamente.
DICA: Alm de rodar a aplicao, voc pode explorar um pouco o
emulador, para
conhecer o sistema operacional Android, caso ainda no conhea.
Durante o
desenvolvimento, o emulador ser seu melhor amigo, ento essa a
oportunidade para
conhec-lo bem.
-
Trabalhando com layouts XML em Android
17 de abril de 2010 20 comentrios
Ol pessoal, hoje vou falar sobre a definio do layout de sua
aplicao Android
usando XML.
Definir os layouts atravs dessa tcnica a forma mais comum de se
comear o
desenvolvimento de uma aplicao.
Para exemplificar os estudos que faremos a partir de agora,
usaremos uma aplicao de
exemplo, que construiremos juntos, a partir dos prximos
posts.
A idia fazer uma aplicao onde o usurio poder entrar com algumas
anotaes, e
visualizar as ltimas entradas.
O layout ser mais ou menos assim:
esboo do layout
Criando o main.xml
Para novos projetos android, o arquivo main.xml j
automaticamente criado. Ele fica
no diretrio res/layout, com o contedo:
view plaincopy to clipboardprint?
1. 2. 7.
-
8. android:layout_width="fill_parent" 9.
android:layout_height="wrap_content" 10.
android:text="@string/hello" 11. /> 12.
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. (No se preocupe, falaremos sobre strings e
seus ids frente
nesse curso)
Para criar um layout parecido com o rascunho do incio do post,
iremos inserir outros
dois elementos:
EditText uma caixa de texto onde o usurio ir entrar com as
anotaes; ListView uma lista de anotaes previamente submetidas.
Assim, o nosso novo XML:
view plaincopy to clipboardprint?
1. 2. 7. 13. 14. 19. 20.
Carregando o arquivo XML na aplicao
Para que a nossa aplicao tenha o layout definido pelo arquivo
XML, preciso
carreg-lo.
Isso feito atravs da funo setContentView(), como no cdigo
abaixo:
view plaincopy to clipboardprint?
1. public void onCreate(Bundle savedInstanceState) { 2.
super.onCreate(savedInstanceState); 3.
setContentView(R.layout.main); 4. }
-
O parmetro R.layout.main indica que o arquivo de layout a ser
carregado o main.xml.
(Se o se arquivo se chamar abobrinha.xml, o parmetro dever ser
R.layout.abobrinha)
possvel 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 prximo post iremos acrescentar um boto a este layout, e
iremos aprender um
pouco mais sobre os parmetros de um documento XML.
DICA: Existe uma ferramenta online, gratuita, para edio de
arquivos de layout XML.
o DroidDraw.
LEITURA RECOMENDADA: Para aprender mais sobre a definio de
layout de
aplicaes android, visite a pgina User Interface da documentao
oficial. (em ingls)
-
Trabalhando com layouts XML em Android (II)
22 de abril de 2010 23 comentrios
Ol a todos!
No post passado aprendemos como criar um documento XML que
define o layout de
uma aplicao Android.
Hoje iremos adicionar um boto a este layout, que permita ao
usurio inserir novas
anotaes.
Adicionando um boto a um layout android
Para adicionar um boto clicvel 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 espao disponvel.
Ao invs disso, vamos usar 240 pixels.
2. Inserir o boto logo aps a caixa de texto. 3. Inserir um novo
LinearLayout que ir conter a caixa de texto e o boto. Isso
necessrio 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 no isso que queremos para estes dois elementos (a caixa
de texto e o
boto queremos que fiquem lado a lado).
Assim, temos o nosso novo main.xml:
view plaincopy to clipboardprint?
1. 2. 7. 11. 17. 18.
-
22. android:text="Inserir" 23. > 24. 25. 26. 31. 32.
Compilando as alteraes, temos a seguinte tela:
Novo layout da aplicao android - agora com o boto "Inserir"
No prximo post veremos como controlar o boto de inserir.
-
Activity o que isso?
2 de maio de 2010 30 comentrios
Hoje iremos conhecer uma das mais importantes classes de uma
aplicao 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 usurio).
Todo aplicativo android comea por uma Activity.
Ou seja, quando uma aplicao android executada, na verdade a sua
Activity
principal que lanada.
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 importncia para o correto
entendimento do
funcionamento de uma aplicao 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
funes:
onCreate() a primeira funo a ser executada quando uma Activity
lanada.
Geralmente a responsvel por carregar os layouts XML e outras
operaes de
inicializao. executada somente uma vez durante a vida til da
Activity.
onStart() chamada imediatamente aps a onCreate() e tambm quando
uma Activity que estava em background volta a ter foco.
onResume() Assim como a onStart(), chamada na inicializao da
Activity
(logo aps a prpria onStart()) e tambm quando uma Activity volta
a ter foco.
Qual a diferena entre as duas? A onStart() s chamada quando a
Activity no
estava mais visvel na tela e volta a ter o foco, enquanto a
onResume() sempre
chamada nas retomadas de foco. onPause() a primeira funo a ser
invocada quando a Activity perde o foco
(ou seja, uma outra Activity vem frente).
onStop() Anloga onPause(), s chamada quando a Activity fica
completamente encoberta por outra Activity (no mais visvel).
onDestroy() A ltima funo a ser executada. Depois dela, a
Activity
considerada morta ou seja, nao pode mais ser relanada. Se o
usurio voltar a requisitar essa Activity, outro objeto ser
contrudo.
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 aplicao executada, a Activity
definida como padro
(na criao do projeto) lanada. Mas eu posso criar outras
Activities?
claro que sim.
-
E para executar outras Activities, basta usar as funes
startActivity() e
startActivityForResult(). No exemplo abaixo, lanamos uma segunda
Activity a partir
da principal, e esperamos um resultado dela como se fosse um
retorno de funo.
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 execuo, a funo
onActivityResult() ser
invocada, com o resultado como parmetro.
Mas como uma Activity define o seu resultado, a ser lido por
aquela que a chamou?
Isso feito invocando-se a funo setResult (int resultCode), como
por exemplo:
1. setResult(Intent.RESULT_OK);
Algum percebeu que eu no disse nada sobre os parmetros da
startActivityForResult()? Isso porque este o assunto do meu
prximo post o mecanismo de Uris em Android. At l!
-
Criando uma Activity secundria
6 de maio de 2010 45 comentrios
No post passado vimos como lanar uma Activity a partir de outra,
usando as funes
startActivity() e startActivityForResult().
Hoje usaremos esta tcnica para mostrar ao usurio uma tela de
Boas Vindas na nossa aplicao de exemplo, o QuickNotes.
Para criar essa nova Activity, usaremos alguma funes do Motodev.
Se voc no est
usando a IDE da Motorola, no tem problema s criar os arquivos
manualmente. Porm recomendo o uso da IDE, por facilitar bastante a
nossa vida.
Vamos comear criando a Activity que dar Boas Vindas ao
usurio.
V at o menu MOTODEV >New > New Android Activity. Na tela
de configurao, entre com o nome da Activity a ser criada:
Configurando a Activity a ser criada
Aps clicar em Finish, j haver a classe WelcomeActivity no
diretrio src do nosso projeto.
Com a Activity criada, o prximo passo criar o arquivo XML que
definir o seu
layout. Crie o arquivo welcome.xml no diretorio res/layout com o
seguinte contedo:
view plaincopy to clipboardprint?
1. 2.
-
6. 13. 14. 20. 21.
Este arquivo XML define uma Activity com um texto e um boto logo
abaixo, com a
palavra Continuar.
Aps criado o arquivo, vamos carreg-lo no mtodo onCreate() da
WelcomeActivity():
1. setContentView(R.layout.welcome);
Lanando a WelcomeActivity
Para lanar a WelcomeActivity a partir da MainActivity, usaremos
a funo
startActivity(). Esta funo recebe como parmetro 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 comunicao entre
Activities.
No cdigo abaixo instanciamos um Intent cuja nica funo lanar
a
WelcomeActivity, e ento o usamos como parmetro para a
startActivity.
Dessa forma, o cdigo 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 boto
-
At agora, j temos a Activity secundria sendo lanada, mas o que
deve acontecer
quando o usurio clicar no boto 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 boto para que
o mtodo finish() seja
invocado ao clique do usurio. O mtodo finish() da classe
Activity fora a morte desta.
O cdigo 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
Assim, finalizamos nossa Activity de Boas Vindas. At o prximo
artigo!
-
Trabalhando com logs em android
13 de maio de 2010 3 comentrios
Hoje irei falar sobre um mecanismo simples porm muito til para
uma aplicao
android: os logs.
Os logs permitem ao desenvolvedor debugar erros durante o
desenvolvimento e tambm
investigar problemas com o software em produo, ou seja, com o
usurio final.
Para este fim, android tem um classe especfica: A classe Log
(android.util.Log).
Para criar os log, temos disposio as funes Log.v(), Log.d(),
Log.i(), Log.w(), r
Log.e().
Mas por que tantas funes?
Porque os log em java tem alguns tipos ou nveis so eles:
DEBUG logs impressos pela funo Log.d() ERROR logs impressos pela
funo Log.e() INFO logs impressos pela funo Log.i() VERBOSE logs
impressos pela funo Log.v() WARN logs impressos pela funo
Log.w()
Todas estas funes recebem como parmetros duas strings a
primeira, chamada de TAG, e a segunda que a mensagem em si. A TAG
uma string que ir identificar a sua
aplicao, tornando mais fcil identificar quais logs foram
impressos por ela. (Todas as
aplicaes imprimem o log no mesmo stream. Assim, a nica forma de
separar os seus
logs filtrando pela sua Tag)
uma boa prtica 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 aplicao, usaremos o DDMS.
Abrindo a aba Logcat, temos a seguinte tela:
-
Lendo logs pelo logcat
No ponto 1 marcado na imagem, temos os botes de filtragem de
nveis. Estes botes 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 ns colocamos no
cdigo 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
aplicao se, por exemplo, forem colocados dentro de um loop.
-
Content Providers
25 de maio de 2010 18 comentrios
Os Content Providers so parte importantssima da arquitetura de
um sistema android.
responsabilidade deles prover s aplicaes o contedo que elas
precisam para
funcionar, ou seja, os dados.
Mas por que so realmente necessrios?
As aplicaes poderiam muito bem acessar diretamente um banco de
dados, por
exemplo. Porm, uma boa prtica tornar o modo como os dados so
gravados
transparente aplicao. Dessa forma, a aplicao pode manter o foco
nas interaes
com o usurio.
Alm disso, essa tcnica permite a criao de Shared Content
Providers, que so
providers pblicos que podem ser acessados por vrias aplicaes.
Por exemplo, existe o content provider de SMS/MMS que permite a
qualquer aplicao ler as
mensagens recebidas por um telefone celular.
E como feita a comunicao entre Content Providers e Aplicaes?
Uri. Guarde bem este nome, pois voc ir precisar muito dele
durante a sua carreira
como desenvolvedor android.
Toda a comunicao entre aplicaes e providers feita atravs dos
mtodos da
interface ContentProvider, que sempre recebem um objeto Uri como
parmetro. 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 mtodos
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 rpida introduo, vamos colocar a mo na massa.
Iremos criar um content provider para o QuickNotes, que servir
para gravar e recuperar
as anotaes do usurio, da seguinte forma:
Intencionalmente coloquei a caixa que
define como o provider ir gravar os dados para mostrar que isso
irrelevante para a
aplicao.
A estrutura das URIs
Uma Uri usada para acessar Content Provider segue o formato:
content://////
Onde authority o nome do provider, e os parmetros so 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 nmero 10.
Criando um Content Provider
Para criar seu prprio content provider, preciso fazer 2
coisas:
1. Criar uma sub-classe da ContentProvider, implementando os
mtodos pblicos que eu citei no comeo do artigo;
2. Registrar o provider no AndroidManifest.xml
Vamos comear 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 possveis
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 e
E assim o nosso Content Provider est pronto para receber
requisies da aplicao.
Ainda no retorna nenhum resultado significativo mas isso faremos
no prximo artigo, onde ensinarei como acessar um banco de dados
SQLite, para fazer esse provider
realmente efetivo.
-
Como usar banco de dados em uma aplicao android
29 de maio de 2010 76 comentrios
Um dos grandes diferenciais da plataforma android a grande
quantidade de mdulos e
APIs que as aplicaes tem disposio para usar. Eles do
muito poder ao desenvolvedores, permitindo que estes faam coisas
que eram
impossveis em outras plataformas mveis.
Um dos mais importantes mdulos 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 mtodos da classe
ContentProvider que
vimos no artigo passado (query(), delete(), update(), etc) para
prover ao usurio os mtodos para criar, atualizar, deletar e
recuperar os dados. Alm disso, usaremos a
classe SQLiteOpenHelper para gerenciar a conexo 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 mtodos
abaixo:
onCreate() Este mtodo chamado quando a conexo com o banco de
dados for aberta pela primeira vez. aqui que criaremos o banco de
dados, com o
comando sql CREATE.
onUpdate() Este mtodo chamado quando a verso do banco de dados
muda. Por exemplo, digamos que voc criou uma nova verso de seu
aplicativo que usa
uma tabela a mais no banco de dados. Quando esta nova verso for
instalada (em
um telefone que j possuir a primeira verso) este mtodo ser
chamado, ento
voc poder criar apenas a nova tabela, mantendo os dados do
usurio.
O cdigo
O cdigo do QuickNotesProvider fica assim, acessando o banco de
dados. A seguir, eu
explico algumas coisas que podem gerar dvidas.
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 anotaes. 31. private
static final String NOTES_TABLE = "notes"; 32. 33. // 'Id' da Uri
referente s notas do usurio. 34. private static final int NOTES =
1; 35. 36. // Tag usada para imprimir os logs. 37. public static
final String TAG = "QuickNotesProvider"; 38. 39. // Instncia da
classe utilitria 40. private DBHelper mHelper; 41. 42. // Uri
matcher - usado para extrair informaes das Uris 43. private static
final UriMatcher mMatcher; 44. 45. private static HashMap
mProjection; 46. 47. static { 48. mProjection = new HashMap(); 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.
/////////////////////////////////////////////////////////////////
60. // Mtodos overrided de ContentProvider // 61.
/////////////////////////////////////////////////////////////////
62. @Override 63. public int delete(Uri uri, String selection,
String[] selectionArgs) { 64. SQLiteDatabase db =
mHelper.getWritableDatabase(); 65. int count; 66. switch
(mMatcher.match(uri)) {
-
67. case NOTES: 68. count = db.delete(NOTES_TABLE, selection,
selectionArgs); 69. break; 70. default: 71. throw new
IllegalArgumentException( 72. "URI desconhecida " + uri); 73. } 74.
75. getContext().getContentResolver().notifyChange(uri, null); 76.
return count; 77. } 78. 79. @Override 80. public String getType(Uri
uri) { 81. switch (mMatcher.match(uri)) { 82. case NOTES: 83.
return Notes.CONTENT_TYPE; 84. default: 85. throw new
IllegalArgumentException( 86. "URI desconhecida " + uri); 87. } 88.
} 89. 90. @Override 91. public Uri insert(Uri uri, ContentValues
values) { 92. switch (mMatcher.match(uri)) { 93. case NOTES: 94.
SQLiteDatabase db = mHelper.getWritableDatabase(); 95. long rowId =
db.insert(NOTES_TABLE, Notes.TEXT, values); 96. if (rowId > 0) {
97. Uri noteUri = ContentUris.withAppendedId( 98.
Notes.CONTENT_URI, rowId); 99.
getContext().getContentResolver().notifyChange( 100. noteUri,
null); 101. return noteUri; 102. } 103. default: 104. throw new
IllegalArgumentException( 105. "URI desconhecida " + uri); 106. }
107. } 108. 109. @Override 110. public boolean onCreate() { 111.
mHelper = new DBHelper(getContext());; 112. return true; 113. }
114. 115. @Override 116. public Cursor query(Uri uri, String[]
projection, String selection, 117. String[] selectionArgs, String
sortOrder) { 118. // Aqui usaremos o SQLiteQueryBuilder para
construir 119. // a query que ser feito ao DB, retornando um cursor
120. // que enviaremos aplicao. 121. SQLiteQueryBuilder builder =
new SQLiteQueryBuilder(); 122. SQLiteDatabase database =
mHelper.getReadableDatabase(); 123. Cursor cursor; 124. switch
(mMatcher.match(uri)) { 125. case NOTES: 126. // O Builer receber
dois parametros: a tabela 127. // onde ser feita a busca, e uma
projection - 128. // que nada mais que uma HashMap com os campos
129. // que queremos recuperar do banco de dados. 130.
builder.setTables(NOTES_TABLE); 131.
builder.setProjectionMap(mProjection); 132. break;
-
133. 134. default: 135. throw new IllegalArgumentException( 136.
"URI desconhecida " + uri); 137. } 138. 139. cursor =
builder.query(database, projection, selection, 140. selectionArgs,
null, null, sortOrder); 141. 142.
cursor.setNotificationUri(getContext().getContentResolver(),
uri);
143. return cursor; 144. } 145. 146. @Override 147. public int
update(Uri uri, ContentValues values, String selection, 148.
String[] selectionArgs) { 149. SQLiteDatabase db =
mHelper.getWritableDatabase(); 150. int count; 151. switch
(mMatcher.match(uri)) { 152. case NOTES: 153. count =
db.update(NOTES_TABLE, values, 154. selection, selectionArgs)
; 155. break; 156. default: 157. throw new
IllegalArgumentException( 158. "URI desconhecida " + uri); 159. }
160. 161. getContext().getContentResolver().notifyChange(uri,
null); 162. return count; 163. } 164. 165.
/////////////////////////////////////////////////////////////////
166. // Inner Classes utilitrias // 167.
/////////////////////////////////////////////////////////////////
168. public static final class Notes implements BaseColumns { 169.
public static final Uri CONTENT_URI = Uri.parse("content://" 170. +
QuickNotesProvider.AUTHORITY + "/notes"); 171. 172. public static
final String CONTENT_TYPE = 173. "vnd.android.cursor.dir/" +
QuickNotesProvider.AUTHORITY; 174. 175. public static final String
NOTE_ID = "_id"; 176. 177. public static final String TEXT =
"text"; 178. } 179. 180. private static class DBHelper extends
SQLiteOpenHelper { 181. 182. DBHelper(Context context) { 183.
super(context, DATABASE_NAME, null, DATABASE_VERSION); 184. } 185.
186. /* O mtodo onCreate chamado quando o provider executado pela
187. * primeira vez, e usado para criar as tabelas no database 188.
*/ 189. @Override 190. public void onCreate(SQLiteDatabase db) {
191. db.execSQL("CREATE TABLE " + NOTES_TABLE + " (" + 192.
Notes.NOTE_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 193.
Notes.TEXT + " LONGTEXT" + ");"); 194. } 195. 196. /* O mtodo
onUpdate invocado quando a verso do banco de dados
-
197. * muda. Assim, usado para fazer adequaes para a aplicao
198. * funcionar corretamente. 199. */ 200. @Override 201. public
void onUpgrade(SQLiteDatabase db, 202. int oldVersion, int
newVersion) { 203. // Como ainda estamos na primeira verso do DB,
204. // no precisamos nos preocupar com o update agora. 205. } 206.
} 207. } 208. 209.
Cursores
O primeiro conceito importante a se falar o conceito dos
Cursores. Como voc deve
percebido, este o tipo de retorno do mtodo query(), e no por
acaso: Os cursores
so apontadores de dados do banco de dados ou seja, uma interface
que permite o acesso aos dados retornados pela query enviada pelo
usurio.
notifyChanges()
Em todos os mtodos em que alteramos o banco de dados (inserimos,
deletamos ou
modificamos dados) importante chamar o mtodo modifyChanges().
Isso far com que
as aplicaes que estejam utilizando este conjunto de dados sejam
notificadas,
permitindo a estas atualizar tambm os dados mostrados ao
usurio.
No prximo post iremos usar o QuickNotesProvider na nossa
aplicao.
-
Criando uma ListActivity
27 de novembro de 2010 39 comentrios
Hoje aprenderemos a usar uma ListActivity para mostrar ao
usuario uma lista com as
anotaes que ele inseriu em nossa aplicao QuickNotes.
ListActivity? O que isso?
A ListActivity uma classe filha da Activity cujo objetivo
mostrar ao usurio uma
Lista (uma ListView). Em suma, uma Activity com alguns mtodos
para
gerenciamento de listas, criada com o intuito de facilitar a
criao de telas com essa
configurao, muito comuns nas aplicaes android.
O primeiro passo para criar uma Activity desse tipo assegurar
que o layout XML a ser
usado por ela possui um elemento ListView (afinal, estamos
criando uma ListActivity!).
Alm disso, preciso que seu id seja @id/android:list.
Adequando o layout da nossa aplicao, temos:
view plaincopy to clipboardprint?
1. 2.
3.
4.
5. 6. 7. 8. 9. 10. 11.
Depois disso, o prximo fazer a nossa classe MainActivity ser uma
classe filha da
ListActivity:
public class MainActivity extends ListActivity
E ento, inicializar a ListView no mtodo onCreate. Mas para isso
precisaremos de um
Adapter.
Adapters
Adapters so classes responsveis por fazer o que chamado de bind:
Receber os dados de um Cursor (ou de alguma outra fonte de dados) e
coloc-los nos seus
respectivos lugares no layout da Activity.
-
Para Activitys complexas, tipicamente so criadas subclasses da
classe CursorAdapter (Adapter dedicado a tratar cursores). No nosso
caso, em que temos um layout bastante
simples, suficiente o uso do SimpleCursorAdapter.
No cdigo abaixo eu mostro como usar este adapter, com uma
explicao sobre seus
parmetros:
view plaincopy to clipboardprint?
1. ListAdapter adapter = new SimpleCursorAdapter( 2. // O
primeiro parametro eh o context. 3. this, 4. // O segundo, o layout
de cada item. 5. R.layout.list_item, 6. // O terceiro parametro eh
o cursor que contem os dados 7. // a serem mostrados 8. mCursor, 9.
// o quarto parametro eh um array com as colunas do 10. // cursor
que serao mostradas 11. new String[]
{QuickNotesProvider.Notes.TEXT}, 12. // o quinto parametro eh um
array (com o mesmo 13. // tamanho do anterior) com os elementos que
14. // receberao os dados. 15. new int[] {R.id.text}); 16. 17.
setListAdapter(adapter);
Um dos parmetros recebidos pelo contrutor da SimpleCursorAdapter
o layout dos
itens da lista, que definimos da seguinte forma:
view plaincopy to clipboardprint?
1. 2.
3. 4.
5. 6. 7.
E por fim, a nossa Activity principal fica assim:
view plaincopy to clipboardprint?
1. package br.com.felipesilveira.quicknotes; 2. 3. import
android.app.ListActivity; 4. import android.os.Bundle; 5. import
android.util.Log; 6. import android.view.View; 7. import
android.view.View.OnClickListener; 8. import android.widget.Button;
9. import android.widget.EditText; 10. import
android.widget.ListAdapter; 11. import
android.widget.SimpleCursorAdapter; 12. import
android.content.ContentValues; 13. import
android.content.Intent;
-
14. import android.database.Cursor; 15. 16. public class
MainActivity extends ListActivity { 17. 18. private static final
String TAG = "QuickNotesMainActivity"; 19. private Cursor mCursor;
20. 21. /** Called when the activity is first created. */ 22.
@Override 23. public void onCreate(Bundle savedInstanceState) { 24.
super.onCreate(savedInstanceState); 25. 26. Log.d(TAG, "Criando a
MainActivity"); 27. 28. setContentView(R.layout.main); 29. 30.
Intent i = new Intent(this, WelcomeActivity.class); 31.
startActivity(i); 32. 33. Button insertButton =
(Button)findViewById(R.id.insert_button); 34.
insertButton.setOnClickListener(mInsertListener); 35. 36. //
adicionando um 'Hint' ao Editbox. 37. EditText editBox =
(EditText)findViewById(R.id.edit_box); 38. editBox.setHint("Nova
nota..."); 39. 40. mCursor = this.getContentResolver(). 41.
query(QuickNotesProvider.Notes.CONTENT_URI, null, null, null,
null); 42. 43. ListAdapter adapter = new SimpleCursorAdapter(
44. // O primeiro parametro eh o context. 45. this, 46. // O
segundo, o layout de cada item. 47. R.layout.list_item, 48. // O
terceiro parametro eh o cursor que contem os
dados 49. // a serem mostrados 50. mCursor, 51. // o quarto
parametro eh um array com as colunas
do 52. // cursor que serao mostradas 53. new String[]
{QuickNotesProvider.Notes.TEXT}, 54. // o quinto parametro eh um
array (com o mesmo 55. // tamanho do anterior) com os elementos que
56. // receberao os dados. 57. new int[] {R.id.text}); 58. 59.
setListAdapter(adapter); 60. } 61. 62. /* 63. * Definindo um
OnClickListener para o boto "Inserir" 64. */ 65. private
OnClickListener mInsertListener = new OnClickListener() { 66.
public void onClick(View v) { 67. EditText editBox =
(EditText)findViewById(R.id.edit_box); 68.
addNote(editBox.getText().toString()); 69. editBox.setText(""); 70.
} 71. }; 72. 73. /* 74. * Mtodo responsvel por inserir um registro
no content provider 75. */ 76. protected void addNote(String text)
{
-
77. ContentValues values = new ContentValues(); 78.
values.put(QuickNotesProvider.Notes.TEXT, text); 79. 80.
getContentResolver().insert( 81.
QuickNotesProvider.Notes.CONTENT_URI, values); 82. } 83. }
Executando a Activity, temos a seguinte tela, com a lista das
notas que o usurio
digitou!
DICA: Tenha muito cuidado ao projetar uma lista. Lembre-se que
um usurio pode
inserir mil, dez mil registros! O qeu aconteceria nessa situao?
A performance nesses
casos um fator a ser muito trabalhado e testado. Mas isso fica
para um prximo
artigo. At l!
-
Trabalhando com Unit Tests em Android
13 de maio de 2011 16 comentrios
Ol pessoal.
Meu nome Athila Santos. Eu trabalho com o Felipe em
desenvolvimento Android na
Motorola e vou comear a ajud-lo na publicao de artigos sobre
Android para este
blog.
O meu primeiro artigo ser sobre Unit Tests.
Para quem no sabe, Unit Tests so testes realizados pelo proprio
desenvolvedor ou
analista de teste, visando testar a menor parte testvel de um
sistema.
No entanto, testes realizados envolvendo contato com o
dispositivo podem ser
realizados por qualquer pessoa que utilize a aplicao. O que ns,
developers, queremos
testar um determinado trecho de cdigo, estado de classes etc.
Para tanto, vamos
desenvolver APLICAES responsveis, unicamente, por testar a nossa
aplicao.
bom frisar que os Unit Tests so parte essencial do ciclo de
desenvolvimento de um
projeto. Para uma app pequena como a QuickNotes pode parecer
perda de tempo, mas
grandes projetos no sobrevivem sem que os desenvolvedores gastem
um bom tempo
desenvolvendo Unit Tests decentes.
A plataforma Java nos fornece uma excelente ferramenta para
desenvolvimento de Unit
Tests. a JUnit. Para ficar ainda melhor, Android tambm nos
fornece todo um
framework de
classes em complemento s j existentes no framework do JUnit.
Dada esta introduo, mos obra!
Neste artigo, vamos testar apenas a UI da Activity principal do
QuickNotes.
Passo 1 Criar o projeto de teste para o QuickNotes.
No Eclipse, clique com o boto direito no seu projeto QuickNotes
-> New -> Other
Expanda a pasta Android e selecione Android Test Project.
Preencha os campos como na figura 1 (ao selecionar o projeto
QuickNotes no campo
An existing Android Project, os campos seguintes sero
preenchidos automaticamente.
-
Clique em Finish e seu projeto ir aparecer no Package
Explorer.
Passo 2 Criar sua classe de test case.
Com o boto direito, clique no projeto QuickNotesTest -> New
-> Class
Preencha os campos conforme a figura 2. (a classe
android.test.ActivityInstrumentationTestCase2 uma classe
presente no framework
de testes do Android que nos prov algumas APIs muito teis para
interagir com a UI
de uma Activity). Ela parametrizada com o nome da classe que ser
testada por esta
classe.
Com a classe de teste criada, vamos implement-la.
A primeira coisa que uma classe de teste precisa de um
construtor. No construtor
padro de qualquer classe de teste que tenha como pai a
classe
ActivityInstrumentationTestCase2, ns precisamos chamar o
construtor desta
-
passando como parametro o pacote da classe sendo testada e uma
instancia
representando tal classe (.class). Para o nosso caso, o
construtor ter apenas isso.
Portanto:
view plaincopy to clipboardprint?
1. public MainActivityTest() { 2.
super("com.exemplos.quicknotes", MainActivity.class); 3. }
O mtodo setUp() um mtodo herdado de uma das classes pai e
destinado
inicializao do estado da classe de teste. Este mtodo executado
ANTES DE CADA
TEST CASE, sempre. No nosso caso, vamos us-lo para pegar os
elementos de UI
da nossa MainActivity:
view plaincopy to clipboardprint?
1. @Override 2. protected void setUp() throws Exception { 3.
super.setUp(); 4. mActivity = getActivity(); // mActivity ira
guardar uma instnca da MainAct
ivity. 5. // Esta chamada so e possivel por causa dos parametros
passados no construt
or. 6. // e o parametro passado na definio da classe 7. mEditor
= (EditText) mActivity.findViewById(R.id.edit_box); 8. mButton =
(Button) mActivity.findViewById(R.id.insert_button); 9. mList =
(ListView) mActivity.findViewById(android.R.id.list); 10. mAdapter
= (SimpleCursorAdapter) mList.getAdapter(); 11. }
Obviamente, voc deve criar as variveis de instncia envolvidas no
mtodo:
view plaincopy to clipboardprint?
1. private MainActivity mActivity; 2. private EditText mEditor;
3. private Button mButton; 4. private ListView mList; 5. private
SimpleCursorAdapter mAdapter;
Vamos tambm criar a seguinte constante na classe (ser explicado
posteriormente)
view plaincopy to clipboardprint?
1. // Altere este valor para o numero de insercoes de teste vc
deseja executar 2. private static int NUMERO_DE_INSERCOES = 10;
Antes de comear a implementar os test cases, importante
verificar se o estado da
classe esta consistente. Para executar checagens, vamos usar
APIs do framework do
JUnit. Mtodos como assertTrue, assertEquals, etc so responsveis
por passar ou falhar
um determinado check.
view plaincopy to clipboardprint?
1. // Testa se os elementos da tela foram inicializados
corretamente 2. private void testPreConditions() { 3.
assertTrue(mActivity != null);
-
4. assertTrue(mEditor != null); 5. assertTrue(mButton != null);
6. assertTrue(mList != null); 7. assertTrue(mAdapter != null); 8.
assertTrue(NUMERO_DE_INSERCOES > 0); 9. }
Agora, podemos comear a escrever nossos test cases. Cada test
case um mtodo
publico, sem retorno (void) e sem parametros. Tambm devem ter
seu nome comeado
pela palavra test. Assim, o Android Test Runner (entidade
responsvel por excutar os testes) saber quais metodos ele deve
executar como testes. Portanto, os test cases
devem ter o seguinte padro:
public void test()
No nosso caso, vamos executar testes de insero de elementos na
lista e verificar se o
que foi digitado pelo usurio o que realmente aparece na
tela.
Para melhorar a exeperincia, altere o arquivo de xml que
descreve o layout da
MainActivity do QuickNotes (QuickNotes\res\layout\main.xml) e
adicione o seguinte
atributo ao ListView:
android:transcriptMode=alwaysScroll
ou seja, deixa o elemento desta forma:
view plaincopy to clipboardprint?
1. 5.
Isto far com que a lista role automaticamente aps uma insero, se
os itens no
couberem mais na tela.
Portanto, nosso test case fica assim:
view plaincopy to clipboardprint?
1. public void testInsertIntoTheList() { 2. // Como nao podemos
assegurar a ordem em que os testes sao executados, deve
mos 3. // verificar pre-condicoes antes de cada teste. 4.
testPreConditions(); 5. 6. for (int i = 0; i <
NUMERO_DE_INSERCOES; i++) { 7. // Codigos de teste que interagem
com elementos da UI (Views) devem rod
ar na thread 8. // principal, tambem chamada de UI thread. 9.
mActivity.runOnUiThread(new Runnable() { 10. @Override 11. public
void run() { 12. // Vamos pedir o foco para o editor e comecar a
digitar 13. mEditor.requestFocus(); 14. } 15. }); 16.
-
17. // Vamos "digitar" no edit box uma entrada com o conteudo
18. // "random[numero aleatorio entre 0 e 100]", navegar para a
direita 19. // (DPAD_RIGHT) - onde esta o botao de Insert e clicar
na center key 20. // para efetuar a insercao 21. Integer rand =
(int)(Math.random()*100); 22. char[] digits =
rand.toString().toCharArray(); 23. if (digits.length == 1) { 24.
this.sendKeys("R A N D O M "+digits[0]+" DPAD_RIGHT
DPAD_CENTER");
25. } else { 26. this.sendKeys("R A N D O M "+digits[0]+"
"+digits[1]+" DPAD_RIGHT D
PAD_CENTER"); 27. } 28. 29. // Apos digitar uma entrada,
comparamos se o que foi digitado pelo usuar
io e 30. // o que esta sendo de fato mostrado na tela.
Lembre-
se que o objetivo aqui e 31. // testar a UI e nao o DB. Android
oferece todo um set de classes specia
l para 32. // executar test cases para operacoes envolvendo o
DB. Por esta razao, n
este momento, 33. // nao nos interessa o que esta guardado no
cursor que foi passado para
o 34. // adapter e sim o que esta sendo mostrado na tela (os
dois devem bater,
obviamente) 35. LinearLayout item =
(LinearLayout)(mList.getChildAt(mList.getChildCount(
) - 1)); 36. TextView view = (TextView)
(item.findViewById(R.id.text)); 37. String text =
view.getText().toString(); 38. assertEquals("random"+rand, text);
39. } 40. }
Repare que executamos uma chamada funcao testPreConditions no
inicio. muito
importante frisar que a ordem em que os testes cases so
executados NO
ASSEGURADA! Portanto, se ns mudssemos o modificador de acesso
dessa funo
para public ela atenderia todos os requisitos para ser um test
case (sem retorno, sem parametros e comecado pela palavra test) e
seria executado pelo Android Test Runner.
No entanto, ningum garante que este mtodo seria executado antes
do nosso teste.
Portanto, a regra : todos os test cases que escrevemos devem ser
independentes entre si
e no possuir nenhuma relao uns com os outros. Caso voc queira
garantir uma ordem
de execuo, deve fazer como fizemos neste exemplo tornar a funo
private e cham-la no inicio de cada test case.
Veja tambm que usamos a API sendKeys da nossa classe-pai
ActivityInstrumentationTestCase2. Esta API envia eventos de
teclas para o
dispositivo como se fosse o proprio usurio que o estivesse
fazendo.
Neste post vamos desenvolver apenas este teste. medida que novas
funcionalidades
forem adicionadas aplicao, vamos desenvolver novos testes para a
UI e publicar em
artigos posteriores.
Passo 3 Executando os testes
-
Executar os testes muito simples: clique com o boto direito no
projeto
QuickNotesTest -> Run As -> Android JUnit Test
Se suas configuraes de AVD estiverem corretas, o simulador ser
iniciado e o
QuickNotes ser executado. Voc ver vrias entradas sendo digitadas
no text field e adicionadas lista uma a uma.
No Eclipse vc pode acompanhar o andamento e o resultado dos
testes, como na figura
abaixo.
Para efeito de demonstrao, vamos forar uma falha. Altere a
seguinte linha do
testcase:
- assertEquals(random+rand, text); +
assertEquals(test_falha+rand, text);
Agora, clique no boto Rerun Test (icone verde com uma seta
amarela). Voc ver o resultado como na figura abaixo. Repare que o
Eclipse te fornece a stack trace de onde
ocorreu a falha. Assim, voc capaz de saber exatamente onde
ocorreu a falha e corrigir
o problema.
Isto termina nossa introduo aos Unit Tests em Android. No prximo
post, vamos
aprender como testar a consistncia de estado de uma Activity (se
o usurio recebe uma
ligao, por exemplo, enquanto est usando sua aplicao, como saber
se sua Activity
vai retornar ao estado anterior depois que a ligao se
encerrar?).
At l!