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 mProjection;

    static {
		mProjection = new HashMap();
		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


Posts Relacionados

  1. Acessando um Content Provider
  2. Criando uma ListActivity
  3. Content Providers
  4. Criando uma Activity secundária

67 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.

Deixe um comentário