Curso Android – Base de dados e AsyncTasks

Para finalizar os tutoriais de introdução a android, era impensável não se falar de Base de Dados. O tutorial irá usar como base o tema de clubes de futebol que temos vindo a utilizar. Para isso, iremos adicionar 3 clubes inicialmente na base de dados e, depois iremos demonstrar como é que se carregam os dados, como adicionar e/ou remover um clube.

Gradle file

O primeiro grande passo para o tutorial, uma vez que vamos usar sqlcipher é o leitor dirigir-se ao ficheiro gradle do módulo e, nas dependencias acrescentar a seguinte:

dependencies {
    (...)
    compile 'net.zetetic:android-database-sqlcipher:3.3.1-1@aar'
}

Isto irá permitir o utilizador utilizar o sqlcipher, fazendo o import do mesmo.

Classe da Base Dados

O próximo passo será criar uma classe que nos irá servir de apoio para todo o processo. Para realizar isso, o utilizador deverá acrescentar a classe da seguinte forma e com o nome de classe a ser “BaseDados”:

classes

Esta classe irá dar-nos todo o apoio necessário. Irá conter o nome da base de dados, o nome das tabelas e seus respectivos campos. Também irá neste caso criá-la caso ainda não exista e, preenche-la com dados que iremos inserir manualmente. A classe deverá ter o seguinte aspecto:

import android.content.ContentValues;
import android.content.Context;

// Use import android.database.sqlite.SQLiteDatabase;
// import android.database.sqlite.SQLiteOpenHelper; for normal support, with no encryption

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;

public class BaseDados extends SQLiteOpenHelper {
    private static final String PASS_HARDCODED =
            "hard-coded password - Nao fazer isto em ambientes de producao";
    private static final String NOME_BASEDADOS = "clubes.db";
    private static final int SCHEMA = 1;

    //We could put those on a contract class container...
    static final String NOME = "nome";
    static final String SIGLA = "sigla";
    static final String TABELA = "clubes";

    //SCHEMA Version is 1
    public BaseDados(Context context) {
        super(context, NOME_BASEDADOS, null, SCHEMA);

        SQLiteDatabase.loadLibs(context);
    }

    //Called on db creation, thanks to getReadableDatabase or getWriteableDatabase
    @Override
    public void onCreate(SQLiteDatabase db) {
        //Table creation !!!
        try {
            db.beginTransaction();
            db.execSQL("CREATE TABLE clubes (nome TEXT, sigla TEXT);");

            ContentValues cv = new ContentValues();

            cv.put(NOME, "Futebol Clube do Porto");
            cv.put(SIGLA, "FCP");
            db.insert(TABELA, NOME, cv);

            cv.put(NOME, "Sport Lisboa e Benfica");
            cv.put(SIGLA, "SLB");
            db.insert(TABELA, NOME, cv);

            cv.put(NOME, "Sporting Clube de Portugal");
            cv.put(SIGLA, "SCP");
            db.insert(TABELA, NOME, cv);


            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion,
                          int newVersion) {
        throw new RuntimeException("Erro");
    }

    SQLiteDatabase getReadableDatabase() {
        return (super.getReadableDatabase(PASS_HARDCODED));
    }

    SQLiteDatabase getWritableDatabase() {
        return (super.getWritableDatabase(PASS_HARDCODED));
    }
}

Layouts

O layout da activity_main irá ser bastante simples, irá ter um spinner para mostrar dados e dois botões, um para adicionar e outro para remover os clubes. Para uma primeira fase, os botões estarão inactivos. O layout deverá ter o seguinte aspecto:

    <Spinner
        android:id="@+id/spinner"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:spinnerMode="dropdown"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Adicionar Clube"
        android:id="@+id/btnAdd"
        android:layout_below="@+id/spinner"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="45dp"
        android:onClick="AdicionarClube"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Remover Clube"
        android:id="@+id/delete"
        android:layout_below="@+id/btnAdd"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="45dp"
        android:onClick="RemoverClube"/>

O próximo passo deverá ser criar um ficheiro .xml com o nome de row. Este ficheiro irá indicar a estrutura do tipo de dados que nós queremos mostrar. Neste caso, será mostrar o nome e a sigla do clube, logo dois “TextView” serão necessários. O código será o seguinte:

<TextView
        android:id="@+id/Nome"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        />
    <TextView
        android:id="@+id/Sigla"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        />

MainActivity

Para finalizar a fase inicial de bases de dados, o código java da MainActivity deverá ter o aspecto indicado em baixo. Importa clarificar o seguinte:

  • Cursor current -> cursor com os dados actual
  • BaseDados bd -> Base de dados criada
  • AsyncTask task -> uma tarefa que irá correr “de parte” para inserir/eliminar dados. Esta é importante, sempre que queremos fazer uma ação à Base de Dados deveremos usar AsyncTasks para não haver conflitos com a thread principal da aplicação
  • Spinner meuSpinner -> spinner de dados. Se o leitor não sabe o que é um spinner, poderá ver aqui.
  • Classe BaseTask -> será a classe que irá correr as AsyncTask para operar sobre a base de dados e, a mesma terá uma função de Load sobre os dados que já contem na base de dados.
public class MainActivity extends AppCompatActivity {
    private Cursor current = null;
    private BaseDados bd = null;
    private AsyncTask task = null;
    Spinner meuSpinner = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SimpleCursorAdapter adapter =
                new SimpleCursorAdapter(this, R.layout.row,
                        current, new String[]{
                        BaseDados.NOME,
                        BaseDados.SIGLA},
                        new int[]{R.id.Nome, R.id.Sigla},
                        0);
        meuSpinner = (Spinner) findViewById(R.id.spinner);
        meuSpinner.setAdapter(adapter);

        if (current == null) {
            bd = new BaseDados(this);
            task = new LoadCursorTask().execute();
        }
    }

    abstract private class BaseTask<T> extends AsyncTask<T, Void, Cursor> {
        @Override
        public void onPostExecute(Cursor result) {
            ((CursorAdapter) meuSpinner.getAdapter()).changeCursor(result);
            current = result;
            task = null;
        }


        //Check http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#query(java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String)
        //We are also retrieving the ROWID, apart from the title and value columns
        //Return results ordered by title column
        protected Cursor doQuery() {
            Cursor result =
                    bd
                            .getReadableDatabase()
                            .query(BaseDados.TABELA,
                                    new String[]{"ROWID AS _id",
                                            BaseDados.NOME,
                                            BaseDados.SIGLA},
                                    null, null, null, null, BaseDados.NOME);

            result.getCount();

            return (result);
        }
    }

    //Wrapper classes for insertion and data loading
    private class LoadCursorTask extends BaseTask<Void> {
        @Override
        protected Cursor doInBackground(Void... params) {
            return (doQuery());
        }
    }
}

O leitor pode agora correr a aplicação e verificar que o spinner criado se encontra preenchido com o nome dos 3 clubes que adicionámos no content values.

Remover Clubes

Para criar o remover, o utilizador deverá acrescentar código na classe BaseTask para criar uma tarefa de remoção.

Para isso, logo a seguir ao método LoadCursorTask, deverá acrescentar o seguinte:

private class DeleteTask extends BaseTask<Cursor> {
        @Override
        protected Cursor doInBackground(Cursor... values) {
            String rowid=values[0].getString(values[0].getColumnIndex("_id"));
            bd.getWritableDatabase().delete(BaseDados.TABELA,
                    "ROWID" + "=" + rowid, null);

            return(doQuery());
        }
    }

Esta função deverá depois ser chamada quando o botão de remoção é utilizado. Para isso o leitor deverá criar um método para o botão de remover e codificá-lo de forma a que consiga ir buscar o item do spinner e eliminá-lo da seguinte forma:

public void RemoverClube(View view) {
        final Cursor cr = (Cursor) meuSpinner.getSelectedItem();
        task=new DeleteTask().execute(cr);
    }

Adicionar Clubes

Neste ponto, iremos ser mais “preguiçosos” e adicionar novamente manualmente. O leitor poderá criar edit text para ir buscar o texto e adicionar vários clubes.

Da mesma forma que criámos uma task para o delete, teremos de criar outra para a inserção, da seguinte forma:

private class InsertTask extends BaseTask<ContentValues> {
        @Override
        protected Cursor doInBackground(ContentValues... values) {
            bd.getWritableDatabase().insert(BaseDados.TABELA,
                    BaseDados.NOME, values[0]);

            return(doQuery());
        }
    }

Para dicionar, terá de criar um Content Values para inserir 2 valores (nome e sigla dos clubes) e enviá-lo para a InsertTask da seguinte forma (isto no evento do botão adicionar):

public void AdicionarClube(View view) {
        ContentValues values=new ContentValues(2);

        values.put(BaseDados.NOME, "Sporting Clube de Braga");
        values.put(BaseDados.SIGLA, "SCB");

        task=new InsertTask().execute(values);
    }

O leitor poderá correr a aplicação e, terá uma base de dados operacional para adicionar  e remover clubes de futebol.

O que gostaria de ver no próximo tutorial sobre Android?

Deixe as suas sugestões nos comentários!

Fonte das imagens

    • eu gostaria de uma seria de tutorias, sobre uma aplicação dessa forma so que busque em um banco de dados em um servidor restFul, pois esta com muita demanda disso hj no mercado e nao se acha esses tutorias em portugues, o servidor pode ser em qualquer linguagem que nao se use bibliotecas prontas, por que utilizamos muito PHP e seria mais interessante algo na mao para entender o sistema restFUL

      • Caro Diego, obrigado pelo comentário.
        Esperamos em breve lançar um tutorial onde iremos explicar a utilização de Webservices com android (Servidor e aplicação)
        Aconselhamos a que faça a subscrição por email, para que seja logo alertado quando o tutorial sair.

        Qualquer dúvida disponha.

    • Bom dia Diego. Hoje lançamos o nosso novo artigo sobre Android, que trata sobre web services. Pode dar uma olhada -> http://dlvr.it/KJmSJH