Bom galera, vamos falar um pouco sobre como criar e utilizar um banco de dados em uma aplicação android. Por padrão, o banco de dados utilizado é o SQLite. Imagino que você deve estar se perguntando, mas Eduardo, o que é o SQLite?
Em uma breve explicação, o SQLite é uma biblioteca em linguagem C, open source, que implementa um banco de dados SQL embutido, ou seja, não tem processo servidor separado, lendo e escrevendo diretamente no arquivo de banco de dados no disco. O android utiliza o SQLite por ser uma biblioteca leve, utilizando dentre 250kb a 500kb, sendo multi-plataforma, onde você pode copiar livremente um banco de dados entre sistemas de 32bits e 64bits.
O SQLite está disponível em todos os dispositivos android, utilizá-lo não requer qualquer configuração ou administração de banco de dados, você precisa somente definir os comandos SQL para criar e atualizar o banco. Depois disso, o banco de dados é gerenciado automaticamente para você pela plataforma Android. Quando criado, o banco de dados por padrão é armazenado no diretório /data/data/<nome-do-pacote-utilizado>/databases/nome-do-arquivo.sqlite.
Bom, vamos à parte divertida do post e o motivo de estarem lendo até agora, hora do Hands On!
package com.agenda; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteCursor; import android.database.sqlite.SQLiteCursorDriver; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQuery; import android.util.Log; public class ContextoDados extends SQLiteOpenHelper { /** O nome do arquivo de base de dados no sistema de arquivos */ private static final String NOME_BD = "Agenda"; /** A versão da base de dados que esta classe compreende. */ private static final int VERSAO_BD = 2; private static final String LOG_TAG = "Agenda"; /** Mantém rastreamento do contexto que nós podemos carregar SQL */ private final Context contexto; public ContextoDados(Context context) { super(context, NOME_BD, null, VERSAO_BD); this.contexto = context; } @Override public void onCreate(SQLiteDatabase db) { String[] sql = contexto.getString(R.string.ContextoDados_onCreate).split("\n"); db.beginTransaction(); try { // Cria a tabela e testa os dados ExecutarComandosSQL(db, sql); db.setTransactionSuccessful(); } catch (SQLException e) { Log.e("Erro ao criar as tabelas e testar os dados", e.toString()); } finally { db.endTransaction(); } } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(LOG_TAG, "Atualizando a base de dados da versão " + oldVersion + " para " + newVersion + ", que destruirá todos os dados antigos"); String[] sql = contexto.getString(R.string.ContextoDados_onUpgrade).split("\n"); db.beginTransaction(); try { ExecutarComandosSQL(db, sql); db.setTransactionSuccessful(); } catch (SQLException e) { Log.e("Erro ao atualizar as tabelas e testar os dados", e.toString()); throw e; } finally { db.endTransaction(); } // Isto é apenas didático. Na vida real, você terá de adicionar novas colunas e não apenas recriar o mesmo banco onCreate(db); } /** * Executa todos os comandos SQL passados no vetor String[] * @param db A base de dados onde os comandos serão executados * @param sql Um vetor de comandos SQL a serem executados */ private void ExecutarComandosSQL(SQLiteDatabase db, String[] sql) { for( String s : sql ) if (s.trim().length()>0) db.execSQL(s); } /** Retorna um ContatosCursor ordenado * @param critério de ordenação */ public ContatosCursor RetornarContatos(ContatosCursor.OrdenarPor ordenarPor) { String sql = ContatosCursor.CONSULTA + (ordenarPor == ContatosCursor.OrdenarPor.NomeCrescente ? "ASC" : "DESC"); SQLiteDatabase bd = getReadableDatabase(); ContatosCursor cc = (ContatosCursor) bd.rawQueryWithFactory(new ContatosCursor.Factory(), sql, null, null); cc.moveToFirst(); return cc; } public long InserirContato(String nome, String telefone, String endereco) { SQLiteDatabase db = getReadableDatabase(); try { ContentValues initialValues = new ContentValues(); initialValues.put("Nome", nome); initialValues.put("Telefone", telefone); initialValues.put("Endereco", endereco); return db.insert("Contatos", null, initialValues); } finally { db.close(); } } public static class ContatosCursor extends SQLiteCursor { public static enum OrdenarPor{ NomeCrescente, NomeDecrescente } private static final String CONSULTA = "SELECT * FROM Contatos ORDER BY Nome "; private ContatosCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable, SQLiteQuery query) { super(db, driver, editTable, query); } private static class Factory implements SQLiteDatabase.CursorFactory { @Override public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable, SQLiteQuery query) { return new ContatosCursor(db, driver, editTable, query); } } public long getID() { return getLong(getColumnIndexOrThrow("ID")); } public String getNome() { return getString(getColumnIndexOrThrow("Nome")); } public String getEndereco() { return getString(getColumnIndexOrThrow("Endereco")); } public String getTelefone() { return getString(getColumnIndexOrThrow("Telefone")); } } }
A classe java ContextoDados é a classe que está criando o banco de dados e suas tabelas, esta classe está herdando de SQLiteOpenHelper(classe auxiliar para criação de banco de dados e gerenciamento de versão), sobrescrevendo (@Override) os métodos onCreate e onUpgrade. Como podemos ler no post do nosso amigo Marlon Ávila sobre Ciclo de Vida Android, o método onCreate é chamado automaticamente quando a aplicação é aberta pela primeira vez, tendo como tarefa criar a base de dados. Como todas as aplicações podem ter novas versões, utilizamos o método onUpdate para fazer a atualização da base de dados. O papel do método ExecutarComandosSQL é facilitar a execução dos comandos SQL.
No Android é possível fazer a personalização de cursores, sendo cada cursor personalizado uma classe dentro da classe ContextoDados, e essa personalização foi utilizada na classe ContatosCursor, para reduzir a dependência de código, escondendo a informação de operação de dados específicas dentro deste cursor personalizado. O método RetornarContatos tem a função de retornar um ContatosCursor preenchido com os contatos do banco de dados, sendo possível ordenar os contatos por Nome em ordem crescente ou decrescente.
Como o foco neste post é a criação e utilização de banco de dados, foi feita uma interface simples, se quiser aprofundar no assunto de Layouts para Android, visite o post do nosso amigo Odair Fernandes sobre o assunto.
Abaixo temos um anexo de como a tela principal ficou, e o código XML utilizado para deixá-la desta maneira, nesta tela foram usados duas TextView, uma mostrando um título para a página e outra para mostrar os contatos salvos no banco e um botão que irá direcionar para a tela do formulário, onde serão informados os dados dos usuários:
<?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:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/contatos" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/listaContatos" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Nenhum contato cadastrado." /> <Button android:id="@+id/btnNovo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Novo Cadastro" /> </LinearLayout>
Agora veremos como ficará a Activity principal que por default recebe o nome de MainActivity e em seguida falaremos sobre os métodos listados nela.
package com.agenda; import com.agenda.ContextoDados.ContatosCursor; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity { Button btnSalvar, btnCancelar, btnNovo; EditText txtNome, txtEndereco, txtTelefone; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); CarregarInterfaceListagem(); } public void CarregarInterfaceListagem() { setContentView(R.layout.main); //configurando o botão de criar novo cadastro btnNovo = (Button)findViewById(R.id.btnNovo); btnNovo.setOnClickListener(new OnClickListener(){ public void onClick(View v) { CarregarInterfaceCadastro(); }}); CarregarLista(this); } public void CarregarInterfaceCadastro() { setContentView(R.layout.cadastro); //configurando o botão de cancelar cadastro btnCancelar = (Button)findViewById(R.id.btnCancelar); btnCancelar.setOnClickListener(new OnClickListener(){ public void onClick(View v) { CarregarInterfaceListagem(); }}); //configurando o formulário de cadastro txtNome = (EditText)findViewById(R.id.txtNome); txtEndereco = (EditText)findViewById(R.id.txtEndereco); txtTelefone = (EditText)findViewById(R.id.txtTelefone); //configurando o botão de salvar btnSalvar = (Button)findViewById(R.id.btnSalvar); btnSalvar.setOnClickListener(new OnClickListener(){ public void onClick(View v) { SalvarCadastro(); }}); } public void SalvarCadastro() { ContextoDados db = new ContextoDados(this); db.InserirContato(txtNome.getText().toString(), txtTelefone.getText().toString(), txtEndereco.getText().toString()); setContentView(R.layout.main); CarregarLista(this); } public void CarregarLista(Context c) { ContextoDados db = new ContextoDados(c); ContatosCursor cursor = db.RetornarContatos(ContatosCursor.OrdenarPor.NomeCrescente); for( int i=0; i <cursor.getCount(); i++) { cursor.moveToPosition(i); ImprimirLinha(cursor.getNome(), cursor.getTelefone(), cursor.getEndereco()); } } public void ImprimirLinha(String nome, String telefone, String endereco) { TextView tv = (TextView)findViewById(R.id.listaContatos); if(tv.getText().toString().equalsIgnoreCase("Nenhum contato cadastrado.")) tv.setText(""); tv.setText(tv.getText() + "\r\n" +"Nome: "+ nome + "\n " +"Telefone: "+ telefone + "\n"+ "Endereço: "+endereco); } }
Esta classe concatena as strings em um TextView para simular as listagens das informações que são trazidas no banco de dados. Até aqui podemos apenas efetuar uma busca no banco de dados e listar nesta classe.
Agora chegou a parte divertida do nosso post, iremos fazer a escrita no banco de dados. Para isso, vamos retornar um pouco para nossa classe ContextoDados, olhando este trecho de código, veremos como inserir um registro no banco de Contatos:
public long InserirContato(String nome, String telefone, String endereco) { SQLiteDatabase db = getReadableDatabase(); try { ContentValues initialValues = new ContentValues(); initialValues.put("Nome", nome); initialValues.put("Telefone", telefone); initialValues.put("Endereco", endereco); return db.insert("Contatos", null, initialValues); } finally { db.close(); } }
Veremos a seguir uma tela simples e seu código XML, que cria um pequeno formulário de cadastro de usuários, contendo apenas três TextView para informar os campos a serem preenchidos, três EditText onde o usuário informará as informações e dois botões, um que irá salvar as informações no banco, e outro que voltará para a tela principal sem efetuar nenhuma alteração:
<?xml version="1.0" encoding="utf-8"?> <AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/lblNome" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="8dp" android:layout_y="9dp" android:text="@string/lblNome" android:textAppearance="?android:attr/textAppearanceSmall" /> <TextView android:id="@+id/lblTelefone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="7dp" android:layout_y="52dp" android:text="@string/lblTelefone" android:textAppearance="?android:attr/textAppearanceSmall" /> <TextView android:id="@+id/lblEndereco" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_x="8dp" android:layout_y="95dp" android:text="@string/lblEndereco" android:textAppearance="?android:attr/textAppearanceSmall" /> <EditText android:id="@+id/txtNome" android:layout_width="180dp" android:layout_height="40dp" android:layout_x="78dp" android:layout_y="8dp" android:inputType="textPersonName" > <requestFocus /> </EditText> <EditText android:id="@+id/txtTelefone" android:layout_width="180dp" android:layout_height="40dp" android:layout_x="78dp" android:layout_y="50dp" android:inputType="phone" /> <EditText android:id="@+id/txtEndereco" android:layout_width="180dp" android:layout_height="40dp" android:layout_x="78dp" android:layout_y="93dp" android:inputType="textPostalAddress" /> <Button android:id="@+id/btnSalvar" android:layout_width="82dp" android:layout_height="wrap_content" android:layout_x="173dp" android:layout_y="142dp" android:text="@string/btnSalvar" /> <Button android:id="@+id/btnCancelar" android:layout_width="92dp" android:layout_height="wrap_content" android:layout_x="78dp" android:layout_y="141dp" android:text="Cancelar" /> </AbsoluteLayout>
E esta será a tela inicial já com os dados recebidos:
Link para download do projeto : Agenda
Conclusão:
Bom galera, neste post podemos ver de uma forma bem simples como fazer a persistência de dados utilizando uma conexão com o banco SQLite que é utilizado pelo android, descobrimos que realizar essa ação é mais simples do que realmente parece ser, sendo apenas uma questão de leitura e efetuar testes. Obrigado a todos por lerem e até breve o/
Boa tarde Eduardo,
Gostaria de uma ajuda sua se possível, eu utilizei seu projeto para estudar e as únicas alterações que fiz foi adicionar mais variáveis (Telefone2, CPF, Email) o código não apresentou problemas, porém na execução dá erro UNFORTUNATELY, AGENDA HAS STOPPED.
Já revisei o código várias vezes mas não encontrei o problema, se puder me ajudar eu agradeço.
Fico no aguado!
Olá Max, você ainda necessita de ajuda?
Olá Eduardo!
Gostei do seu post, apesar de ser desenvolvedor java a anos, estou iniciando em projetos android. Tomei liberdade de usar seu projeto para estudos, funcionou bem, porém tive que fazer uma alteração na rotina de salvar, pois estava falhando no recarregamento da tela de cadastro após a inserção de dados. a seguir coloco o que modifiquei ok?
public void SalvarCadastro()
{
ContextoDados db = new ContextoDados(this);
db.InserirContato(txtNome.getText().toString(), txtTelefone.getText().toString(), txtEndereco.getText().toString());
//setContentView(R.layout.main); <—– tirei
//CarregarLista(this); <—– tirei
CarregarInterfaceListagem(); //<—-coloquei
}
Com essa modificação funcionou bem.
att. Reinaldo Souza (RAS)
Reinaldo, legal a sua solução! Dependendo do projeto podemos aplicar padrões de projetos para melhorar a reutilização de códigos em Java nos aplicativos Android
Cara, eu fui executar o plicativo, ele nem abriu, disse q o aplicativo parou…=\ você sabe me dizer o que aconteceu?
Abraço, kenji
Lucas, quando a mensagem o aplicativo parou aparece, pode ser por vários motivos.
Uma forma de você localizar o ponto que está causando a falha no aplicativo é analisar os logs em tempo de execução.
O ADT Android em conjunto com o SDK tem uma classe pronta para isso.
Para saber mais detalhes, acesse http://developer.android.com/tools/help/logcat.html
ola!,
gostaria de saber se é possível seguindo este modelo de aplicativo criar um aplicativo que sirva como consulta a um banco de dados. Exemplo: tenho uma tabela com uns itens e pretendo criar um aplicativo que eu digite um item e ele faça a procura e me de os dados deste item.
Olá amigos, estou iniciando nesse mundo de desenvolvimento android e gostaria de saber se existe uma forma de integrar o crm do meu site com um app, diariamente enviamos para nossos clientes (pais) informações dos alunos, gostária de saber se existe uma forma de fazer isso sem ter que ficar atualizando a versão do app todo o dia. Desde já agradeço.
Olá, sim é perfeitamente possível. Irei lhe enviar um e-mail privado para contato a respeito.
Olá! O tutorial esta ótimo mas eu gostaria de alem de inserir, ter a opção de editar e deletar os cadastros. É muito dificil de fazer isso? Estou precisando de algo parecido para um trabalho mas não estou conseguindo chegar na solução…
Obrigado desde já!
Bom Dia. Gostaria de saber se existe uma solução para enviar os dados do banco no sqlite para um webservice. Estou começando e não consigo achar nada relacionado. Obrigado. Ótimo Post.
Bom dia Gerson, existe sim como enviar para um webservice os dados do banco. Alias, isso é uma das funcionalidades mais importantes hoje em dia dos app mobile. Em breve teremos mais posts sobre mobile no blog da Fábrica de Software.