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

Um dos grandes diferenciais da plataforma android é a grande quantidade de módulos e APIsSQLite 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.

package br.com.felipesilveira.quicknotes;

import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.Context;
import android.content.UriMatcher;
import android.net.Uri;
import android.provider.BaseColumns;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;

public class QuickNotesProvider extends ContentProvider {

	// Authority do nosso provider, a ser usado nas Uris.
	public static final String AUTHORITY = 
        "br.com.felipesilveira.quicknotes.quicknotesprovider";
	
	// Nome do arquivo que irá conter o banco de dados.
	private static  final String DATABASE_NAME = "quicknotes.db";
	
	// Versao do banco de dados.
	// Este valor é importante pois é usado em futuros updates do DB.
	private static  final int  DATABASE_VERSION = 1;
	
	// Nome da tabela que irá conter as anotações.
	private static final  String NOTES_TABLE = "notes";

	// 'Id' da Uri referente às notas do usuário.
	private  static final int NOTES = 1;

	// Tag usada para imprimir os logs.
	public static final String TAG = "QuickNotesProvider";
	
	// Instância da classe utilitária
	private DBHelper mHelper;
	
	// Uri matcher - usado para extrair informações das Uris
    private static final UriMatcher mMatcher;

    private static HashMap<String, String> mProjection;
    
    static {
		mProjection = new HashMap<String, String>();
		mProjection.put(Notes.NOTE_ID, Notes.NOTE_ID);
		mProjection.put(Notes.TEXT, Notes.TEXT);  	
    }
    
    static {
    	mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		mMatcher.addURI(AUTHORITY, NOTES_TABLE, NOTES);
    }
    
    
	/////////////////////////////////////////////////////////////////
	//           Métodos overrided de ContentProvider              //
	/////////////////////////////////////////////////////////////////
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mHelper.getWritableDatabase();
        int count;
        switch (mMatcher.match(uri)) {
        	case NOTES:
                count = db.delete(NOTES_TABLE, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException(
                  "URI desconhecida " + uri);
        }
	 
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
	}

	@Override
	public String getType(Uri uri) {
	        switch (mMatcher.match(uri)) {
	            case NOTES:
	                return Notes.CONTENT_TYPE;
	            default:
	                throw new IllegalArgumentException(
                        "URI desconhecida " + uri);
	        }
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
        switch (mMatcher.match(uri)) {
	    	case NOTES:
		        SQLiteDatabase db = mHelper.getWritableDatabase();
		        long rowId = db.insert(NOTES_TABLE, Notes.TEXT, values);
		        if (rowId > 0) {
		            Uri noteUri = ContentUris.withAppendedId(
                                 Notes.CONTENT_URI, rowId);
		            getContext().getContentResolver().notifyChange(
                                 noteUri, null);
		            return noteUri;
		        }
	        default:
	            throw new IllegalArgumentException(
                        "URI desconhecida " + uri);
        }
	}

	@Override
	public boolean onCreate() {
		mHelper = new DBHelper(getContext());;
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
			// Aqui usaremos o SQLiteQueryBuilder para construir
			// a query que será feito ao DB, retornando um cursor
			// que enviaremos à aplicação.
	        SQLiteQueryBuilder builder = new  SQLiteQueryBuilder();
	        SQLiteDatabase database = mHelper.getReadableDatabase();
	        Cursor cursor;
	        switch (mMatcher.match(uri)) {
	            case NOTES:
	            	// O Builer receberá dois parametros: a tabela
	            	// onde será feita a busca, e uma projection - 
	            	// que nada mais é que uma HashMap com os campos
	            	// que queremos recuperar do banco de dados.
	                builder.setTables(NOTES_TABLE);
	                builder.setProjectionMap(mProjection);
	                break;
	 
	            default:
	                throw new IllegalArgumentException(
                          "URI desconhecida " + uri);
	        }
 
	        cursor = builder.query(database, projection, selection, 
	         selectionArgs, null, null, sortOrder);

	        cursor.setNotificationUri(getContext().getContentResolver(), uri);
	        return cursor;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
	        SQLiteDatabase db = mHelper.getWritableDatabase();
	        int count;
	        switch (mMatcher.match(uri)) {
	            case NOTES:
	                count = db.update(NOTES_TABLE, values, 
                                                     selection, selectionArgs);
	                break;	 
	            default:
	                throw new IllegalArgumentException(
                            "URI desconhecida " + uri);
	        }
	 
	        getContext().getContentResolver().notifyChange(uri, null);
	        return count;
	}
	
	/////////////////////////////////////////////////////////////////
	//                Inner Classes utilitárias                    //
	/////////////////////////////////////////////////////////////////
    public static final class  Notes implements  BaseColumns {
        public static final Uri CONTENT_URI = Uri.parse("content://"
	                + QuickNotesProvider.AUTHORITY + "/notes");
	 
        public static final String CONTENT_TYPE = 
                "vnd.android.cursor.dir/" + QuickNotesProvider.AUTHORITY;
	 
        public static final String NOTE_ID = "_id";
	 
        public static final String TEXT = "text";
    }
    
    private static class DBHelper extends SQLiteOpenHelper {
	 
        DBHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
	 
        /* O método onCreate é chamado quando o provider é executado pela
         * primeira vez, e usado para criar as tabelas no database
         */
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE " + NOTES_TABLE + " (" + 
            		Notes.NOTE_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
            		Notes.TEXT + " LONGTEXT" + ");");
        }
	        
        /* O método onUpdate é invocado quando a versão do banco de dados
         * muda. Assim, é usado para fazer adequações para a aplicação
         * funcionar corretamente.
         */	 
        @Override
        public void onUpgrade(SQLiteDatabase db, 
                                      int oldVersion, int newVersion) {
        	// Como ainda estamos na primeira versão do DB,
        	// não precisamos nos preocupar com o update agora.
        }
    }
}

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.

No próximo post iremos usar o QuickNotesProvider na nossa aplicação.

Desenvolvendo para Android

82 respostas to “Como usar banco de dados em uma aplicação android”

  1. Junior De Santi disse:

    Boa noite, Felipe, primeiramente gostaria de agradecer pelo site e todo conhecimento passado.
    Estou me aventurando com o Android (apesar de não saber JAVA, loucura não?rs)Enfim, eu fui seguindo o passo o passo e foi tudo dando certo.
    Fui até o •Trabalhando com layouts XML em Android (II).
    Já na próxima lição eu vi que você não seguiu o mesmo projeto que estavamos criando,gostaria de saber se não tem como você montar as lições na sequencia, sempre aproveitando o que já foi feito na lição anterior?
    Atenciosamente: Junior De Santi.

  2. Robson disse:

    Ele de fato ESTÁ seguindo uma sequencia, muito didática por sinal.

  3. Robson disse:

    Obrigado Israel.

  4. Carlos disse:

    Olá, gostaria de saber se alguem pode me passar a aplicação por download, nao consigo implementar o provider aqui, ele da erro la na parte Static{
    mProjection…

  5. Marcos R. Weimer disse:

    Carlos disse:
    Olá, gostaria de saber se alguem pode me passar a aplicação por download, nao consigo implementar o provider aqui, ele da erro la na parte Static{
    mProjection…

    O código esta incorreto no site,
    SUBSTITUA:
    private static HashMap mProjection;
    POR
    private static HashMap mProjection;

    E TAMBEM SUBSTITUA:
    mProjection = new HashMap();
    POR
    mProjection = new HashMap();

  6. Marcos R. Weimer disse:

    Não saiu a linha toda, cortou a tag,
    em resumo, substitua string por String e remova ‘=””‘ (igual e aspas duplas)

  7. Bruno disse:

    Felipe como faço para usar um banco de dados centralizado em um servidor e os smartphones conectarem remotamente para exibir os dados?

  8. Olá Bruno,
    sim, é possivel, mas você terá que fazer isso através de webservices (ou outra arquitetura semelhante)

  9. Luiz gonzaga disse:

    Muito bom obrigado…

  10. Daniel disse:

    amigos…. qual seria uma sequencia para começar a fazer uma aplicação no banco de dados?
    tipo… quais programas instalar no pc
    o que instalar no aparelho android?

  11. Cristiano Franco disse:

    Olá Felipe, primeiramente parabens pela iniciativa, ajudou bastante gente.

    Felipe, tudo estava indo bem até a seção “Inner Classes utilitárias”, estou usndo o MOTODEV e quando começo a escrever as classes ele acusa erro, a mensagem q aparece no x vermelho ao lado é a seguinte:

    “O tipo público Notes deve ser definido em seu próprio arquivo
    Modificador ilegal para a classe Notes. apenas público, abstract e final são permitidos”

    Está assim tb a classe DBHelper.

    Como eu consigo resolver isso? Um abraço.

  12. bob disse:

    muito bom cara mais sera que voce poderia fazer a conexao ao mysql

  13. Pedro disse:

    Além do erro já corrigido por Marcos, encontro esse erro:

    mMatcher.addURI(AUTHORITY, NOTES_TABLE, NOTES);

    –> parêmtros devem ser string,string e Int

    como resolver?

  14. Pedro disse:

    resolvido: é que eu usei parcialmente o fonte do artigo anterior, e lá AUTHORITY está definido como URI. Achei que o exemplos seriam em sequência.

  15. Rodrigo Borges disse:

    Muito obrigado pelo tutorial! Está ajudando bastante!

  16. Felipe,

    Parabéns, muito bom seu exemplo me ajudou muito, se alguém precisar criei uma variação do exemplo acima em meu blog.

    http://escoladeandroid.blogspot.com.br/2012/02/android-criando-uma-agenda-de-contatos.html

    Obrigado.

  17. Thiago disse:

    e impressao minha ou voce colocou ultimo topico
    “notify changes” e escreveu dentro topico “modify changes” ?

    Corrija pra nos pf.
    minha implementação tbm deu erro mo mproject.
    ate outros tutoriais foi blz, agr nesse complicou hem XD
    mt coisa, ;3

  18. Thiago disse:

    eu vi porque da erro no mProjection.
    porque o nao tem aspas
    e a String esta em minusculo, deve se estar em maiusculo.

    ate !

  19. Felipe disse:

    Cara ótimo material e até a pouco estava resolvendo os problemas sozinho mas agora não sei mais o que fazer, quando clico em inserir ele fecha a aplicação, já refiz e revi tudo e continua sempre igual, pode me dar algum caminho para identificar o erro.

  20. Nielsen disse:

    Pessoal, não consigo de jeito nenhum fazer com que minha aplicação consiga rodar no android, embora no eclipse ele me fale que está tudo ok. Desconfio que seja alguma coisa na hora de utilizar o BD. Não falta alguma função de criação de banco, ou alguma tarefa adicional que eu tenha que fazer…?

  21. Thiago Tavares disse:

    O meu dá erro em algumas declarações, por exemplo:

    private static final UriMatcher mMatcher;
    private DBHelper mHelper;

    Eu ainda não fiz o código todo, será que alguma coisa pra baixo vai corrigir isso?

    /*Esse tutorial não foi tão cuidadoso quanto os anteriores :/
    *os anteriores foram muito bons… */

  22. Thiago Tavares disse:

    Ah! Espera!
    Depois que eu vi que você implementa as classe no final!
    Mil perdões!

  23. Gomez disse:

    Olá gostaria de parabenizá-lo pelo post. Eu gosto muito do que vc escreve.
    POrém me surgiu uma dúvida, como eu faço para criar um banco de de Dados com informações só para serem acessadas(consultadas)
    Por exemplo tenho 3 spinners e um botão. Vou colocar um exemplo
    na 1º Spinner eu teria opções de escola, hospitais, praças (Lugares Públicos)…
    na 2º Spinner eu teria as opções de zonas da cidade,( zona 1, zona2, zona3..)
    na 3º spinner os principais os bairros da cidade( bairro A, bairro B…)
    se a spinner 1 eu escolher Escola
    e a spinner 3 eu escolher o Bairro A,
    e a pessoa clicar no botão pesquisar
    como eu faço para na próxima tela apresentar todas as escolas do Bairro A ??
    Não é pra inserir nada, queria saber como faz um banco de dados apenas para consultar dados inseridos!
    De modo que quando alguém selecionar o Bairro A, na tela seguinte faria uma query sobre esses dados (ex. select * from escola where bairro = ‘A’)
    Como eu faço isso??

  24. DENIS disse:

    Tente uma ferramenta gratuita – Valentina Studio. Produto surpreendente! IMO é o melhor SQLite Manager, para todas as plataformas. http://www.valentina-db.com/en/valentina-studio-overview

  25. Claudio disse:

    É possivel criar o banco de dados no Microsoft Office Access??

  26. Claudio disse:

    E como adiciono ele ao projecto?

  27. Maria disse:

    Olá Felipe, parabens pelo trabalho. Gostaria de saber se tem algum material sobre acesso ao banco de dados no aparelho mobile. Como faço para exportar os dados gravados no smartfone. Ou ainda qual a sua experiencia do uso de bd remoto.

  28. Lucão disse:

    O SQLite é offline? não é possivel varios usuários acessarem ele atravez do app?

  29. web page disse:

    Eu gosto lendo um post que fará homens e mulheres pensa.
    Também, Obrigado por permitindo mim comentar! http://Linconsousa.com/

  30. Flipe Garcia disse:

    Olá Felipe. Obrigado por compartilhar estas informações de forma tão simples. Tenho uma dúvida que poderá complementar o conteúdo analisado. Eu já criei a base e a tabela com a minha aplicação, e salvei vários dados e estão OK. Contudo, se em outra oportunidade (outro dia) abro a aplicação, reconhece a base e a tabela, mas dá erro ao salvar os dados. Ou seja, a ideia é inserir (acrescentar) novos dados na mesma base/tabela o que não está acontecendo. Como faço então?
    Além disso, tenho como saber a quantidade de registros (dados) na tabela?
    Usei estes comandos: INSERT INTO tabela VALUES (….) e REPLACE INTO tabela VALUES (….).
    Obrigado

Deixe um comentário