quarta-feira, 20 de agosto de 2014

Android Tutoriais - ContentProvider

Bom dia pessoal,
Comecei um projeto pessoal, e para facilitar a comunicação com o banco de dados sqlite, eu pesquisei um pouco sobre ContentProvider, e achei válido postar aqui um tutorial, sendo que com um ContentProvider, toda a parte de comunicação com o banco de dados fica bem mais simples.

ContentProvider serve para comunicar com o sqlite no android e retornar cursor com os registros sem a necessidade de montar um sql para todo comando, ele também é responsável por toda operação CRUD (create, read, update, delete) do aplicativo.

Bom para começar vamos definir uma tabela, criando uma classe para isso:

public class TutorialTable {
    //Tabela do BD
    public static final String TABLE_TUTORIAL = "tutorial";
    public static final String COLUMN_ID = "_id";
    public static final String COLUMN_DESCRICAO = "descricao";

    // SQL para criar a tabela
    private static final String DATABASE_CREATE = "create table "
            + TABLE_TUTORIAL
            + "("
            + COLUMN_ID + " integer primary key autoincrement, "
            + COLUMN_DESCRICAO + " text not null "
            + ");";

    public static void onCreate(SQLiteDatabase database) {
        database.execSQL(DATABASE_CREATE);
    }

    public static void onUpgrade(SQLiteDatabase database, int oldVersion,
                                 int newVersion) {
        database.execSQL("DROP TABLE IF EXISTS " + TABLE_TUTORIAL);
        onCreate(database);
    }
}


A classe da tabela é bem simples, primeiro definimos uma variável om o nome da tabela e das colunas (para acessar mais tarde), depois definimos o SQL para criar a tabela, e o upgrade (para quando mudarmos alguma coluna, aqui para facilitar ele da DROP e depois CREATE novamente, mas se não quiser ter perda de dados, poderíamos apenas usar ALTER TABLE para adicionar e remover colunas, porém teríamos que controlar as versões, como aqui é apenas para testes vou utilizar a maneira mais fácil.


Depois criaremos uma classe que será o DatabaseHelper, que fará a criação do BD e a atualização do mesmo.

public class DatabaseHelper extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "tutorial.db";
    private static final int DATABASE_VERSION = 1;

    public IGDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // Método chamado ao criar o BD
    @Override
    public void onCreate(SQLiteDatabase database) {
        TutorialTable.onCreate(database);
    }

    // Método chamado na atualização do BD, se você mudar o DATABASE_VERSION ele irá chamar esse método
    @Override
    public void onUpgrade(SQLiteDatabase database, int oldVersion,
                          int newVersion) {
        TutorialTable.onUpgrade(database, oldVersion, newVersion);
    }
}

Pronto, temos nossa tabela e nosso BD funcionando, lembrando que para cada tabela é bom criar uma classe diferente, e lembrar de adicionar o onCreate e onUpgrade no databasehelper.
Agora só falta a classe que será responsável pela comunicação com o BD, que será nosso ContentProvider.

public class CustomContentProvider extends ContentProvider {
    private DatabaseHelper database;

    private static final String AUTHORITY = "com.caioketo.tutorial.contentprovider";

    // ------- Definimos URIs uma para cada tabela
    private static final String PATH_TUTORIAL = "tutorial";

    public static final Uri CONTENT_URI_TUTORIAL = Uri.parse("content://" + AUTHORITY
            + "/" + PATH_TUTORIAL);


    // ------- Configurando o UriMatcher
    private static final int TUTORIAL = 10;
    private static final int TUTORIAL_ID = 20;
    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        sURIMatcher.addURI(AUTHORITY, PATH_PERGUNTAS, TUTORIAL);
        sURIMatcher.addURI(AUTHORITY, PATH_PERGUNTAS + "/#", TUTORIAL_ID);
    }

    @Override
    public boolean onCreate() {
        return false;
    }

    //@Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {

        // Vamos utilizar o queryBuilder em vez do método query()
        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

        int uriType = sURIMatcher.match(uri);
        switch (uriType) {

            case TUTORIAL_ID:
                // Adicionamos a coluna ID
                queryBuilder.appendWhere(TutorialTable.COLUMN_ID + "="
                        + uri.getLastPathSegment());
                //$FALL-THROUGH$
            case TUTORIAL:
                queryBuilder.setTables(TutorialTable.TABLE_PERGUNTA);
                break;

            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        SQLiteDatabase db = database.getWritableDatabase();
        Cursor cursor = queryBuilder.query(db, projection, selection,
                selectionArgs, null, null, sortOrder);
        // Notificamos caso exista algum Listener
        cursor.setNotificationUri(getContext().getContentResolver(), uri);

        return cursor;
    }

    public String getPATH(int pathID) {
        switch (pathID) {
            case TUTORIAL:
                return PATH_PERGUNTAS;
            default:
                return "";
        }
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        int uriType = sURIMatcher.match(uri);
        SQLiteDatabase sqlDB = database.getWritableDatabase();
        int rowsDeleted = 0;
        long id = 0;
        switch (uriType) {
            case TUTORIAL:
                id = sqlDB.insert(TutorialTable.TABLE_TUTORIAL, null, values);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return Uri.parse(getPATH(uriType) + "/" + id);
    }


    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int uriType = sURIMatcher.match(uri);
        SQLiteDatabase sqlDB = database.getWritableDatabase();
        int rowsDeleted = 0;
        switch (uriType) {
            case TUTORIAL:
                rowsDeleted = sqlDB.delete(TutorialTable.TABLE_TUTORIAL, selection,
                        selectionArgs);
                break;
            case TUTORIAL_ID:
                String id = uri.getLastPathSegment();
                if (TextUtils.isEmpty(selection)) {
                    rowsDeleted = sqlDB.delete(TutorialTable.TABLE_TUTORIAL,
                            TutorialTable.COLUMN_ID + "=" + id,
                            null);
                } else {
                    rowsDeleted = sqlDB.delete(TutorialTable.TABLE_TUTORIAL,
                            TutorialTable.COLUMN_ID + "=" + id
                                    + " and " + selection,
                            selectionArgs);
                }
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsDeleted;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {

        int uriType = sURIMatcher.match(uri);
        SQLiteDatabase sqlDB = database.getWritableDatabase();
        int rowsUpdated = 0;
        switch (uriType) {
            case TUTORIAL:
                rowsUpdated = sqlDB.update(TutorialTable.TABLE_TUTORIAL,
                        values,
                        selection,
                        selectionArgs);
                break;
            case TUTORIAL_ID:
                String id = uri.getLastPathSegment();
                if (TextUtils.isEmpty(selection)) {
                    rowsUpdated = sqlDB.update(TutorialTable.TABLE_TUTORIAL,
                            values,
                            TutorialTable.COLUMN_ID + "=" + id,
                            null);
                } else {
                    rowsUpdated = sqlDB.update(TutorialTable.TABLE_TUTORIAL,
                            values,
                            TutorialTable.COLUMN_ID + "=" + id
                                    + " and "
                                    + selection,
                            selectionArgs);
                }
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return rowsUpdated;
    }

    private void checkColumns(String[] projection) {
        String[] available = { TutorialTable.COLUMN_DESCRICAO, PerguntasTable.COLUMN_ID };
        if (projection != null) {
            HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
            HashSet<String> availableColumns = new HashSet<String>(Arrays.asList(available));
            // check if all columns which are requested are available
            if (!availableColumns.containsAll(requestedColumns)) {
                throw new IllegalArgumentException("Unknown columns in projection");
            }
        }
    }
}


Primeiro definimos a AUTHORITY que será o path do app, para encontrar o BD, depois definimos Uris para cada tabela, e então configuramos o UriMatcher para pegar pelo ID em vez da Uri, para melhorar a velocidade. E então criamos nosso ContentProvider, os métodos são básicos como podem ver:

query(): Seria como um select, retorna um Cursor com os registros encontrados;
getPATH(): Para retornar o PATH pelo ID;
insert(): Como o nome mesmo já diz, insere registros na tabela;
delete(): Deleta registros;
update(): Atualiza os registros;
checkColumns(): esse a gente cria para verificar se a coluna existe antes de executar qualquer comando SQL, caso mude as colunas ele não vai dar erro de SQL, pois trataremos antes.

IMPORTANTE:
Após a criação do ContentProvider temos que declará-lo no AndroidManifest.XML

<provider
            android:authorities="com.caioketo.tutorial.CustomContentProvider"
            android:name=".dao.CustomContentProvider" >
</provider>

E agora sim estamos prontos para trabalhar com o BD, sempre iremos usar o ContentProvider para todas as operações, e podemos até criar um método no ContentProvider para executar um comando SQL (se nescessário) para apenas essa classe se comunicar diretamente com o BD, assim encapsulamos a comunicação.

E aqui finalizo mais um post, qualquer dúvida, sugestão, erro, etc, favor deixar comentários, e até a próxima.


Nenhum comentário:

Postar um comentário