You are currently viewing Curso Android – Base de dados e AsyncTasks

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!

António Sousa

António Sousa, técnico de redes e sistemas informáticos e fundador do Tech em Português! Sou um amante das novas tecnologias e um aventureiro dessa grande "auto-estrada" que é a internet!