Introdução ao SQLite do Android: criando e usando um banco de dados baseado em sqlite3

Neste artigo será apresentado uma das maneiras de acesso direto à banco de dados SQLite no  Android. Com a prática, cada um irá adaptar esse metódo de acordo com o seu jeito pessoal; mas tenha em mente que esse artigo tem o objetivo de ser bem básico, não sendo uma abordagem completa ou exclusiva.

Aqui caminharemos através de um código e por ferramentas de um exemplo bem simples, com o objetivo de inserir e recuperar alguns dados de um banco de dados em uma aplicação Android, e depois examinar o banco de dados usando um shell com o comando sqlite3. O código completo desse exemplo está disponivel em http://totsp.com/svn/repo/AndroidExamples/trunk/.

Primeiro de tudo, precisamos criar uma aplicação Android que possua um banco de dados. Poderiamos usar qualquer aplicação que tenha um banco de dados para explora-la, como com.android.alarmclock, mas iremos optar por criar uma do zero. Depois da configuração, a interface de nossa aplicação parecerá com a tela abaixo:

 

Sim, esse projeto é feito, e tem apenas uma Atividade, mas para nossos própositos nós não precisamos criar uma interface sofisticada (ou qualquer coisa que seja complicada em algum nivel). Para criar esse projeto, usaremos o IDE Eclipse. Além do Eclipse, precisaremos também do SDK do Android com o plugin ADT correto.

1. Para criar um projeto Android básico, simplesmente selecionamos File->New->Other->Android->Android Project. Na caixa de dialogo entramos com o nome da aplicação (AndroidExamples) e o nome do pacote (com.totsp.androidexamples), além de algumas outras configurações, como mostrado na figura abaixo:

 

O target que nós escolhemos precisa ser um que nós tenhamos instalados quando configuramos o SDK do Android. Nesse exemplo, estamos usando o 1.6, por ser ainda a plataforma mais comum em uso nos telefones dos usuários. Precisamos também configurar o valor “Min SDK Version” para 4, que corresponde ao SDK 1.6.

Assim que tivermos as configuração do projeto em seu devido lugar, o próximo passo seria criar um classe que auxilie na criação do banco de dados e encapsule outros detalhes SQL.Nós chamaremos esse classe DataHelper. Dentro dessa classe (quase no final dela) incluimos uma inner-classe importante que dá acesso a um SQLiteOpenHelper. O código completo é mostrado abaixo:

package com.totsp.androidexamples;

im<wbr>port android.content.Context;
impor<wbr>t android.database.Cursor;
impor<wbr>t android.database.sqlite.SQLite<wbr>Database;
import android.database.sqlite.SQLite<wbr>OpenHelper;
import android.database.sqlite.SQLite<wbr>Statement;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class DataHelper {

   private static final String DATABASE_NAME = "example.db";
   private static final int DATABASE_VERSION = 1;
   private static final String TABLE_NAME = "table1";

   private Context context;
   private SQLiteDatabase db;

   private SQLiteStatement insertStmt;
   private static final String INSERT = "insert into "
      + TABLE_NAME + "(name) values (?)";

   public DataHelper(Context context) {
      this.context = context;
      OpenHelper openHelper = new OpenHelper(this.context);
      this.db = openHelper.getWritableDatabase<wbr>();
      this.insertStmt = this.db.compileStatement(INSER<wbr>T);
   }

   public long insert(String name) {
      this.insertStmt.bindString(1, name);
      return this.insertStmt.executeInsert(<wbr>);
   }

   public void deleteAll() {
      this.db.delete(TABLE_NAME, null, null);
   }

   public List<String> selectAll() {
      List<String> list = new ArrayList<String>();
      Cursor cursor = this.db.query(TABLE_NAME, new String[] { "name" },
        null, null, null, null, "name desc");
      if (cursor.moveToFirst()) {
         do {
            list.add(cursor.getString(0));<wbr>
         } while (cursor.moveToNext());
      }
      if (cursor != null && !cursor.isClosed()) {
         cursor.close();
      }
      return list;
   }

   private static class OpenHelper extends SQLiteOpenHelper {

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

      @Override
      public void onCreate(SQLiteDatabase db) {
         db.execSQL("CREATE TABLE " + TABLE_NAME + "
          (id INTEGER PRIMARY KEY, name TEXT)");
      }

      @Override
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
         Log.w("Example", "Upgrading database, this will drop tables and recreate.");
         db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
         onCreate(db);
      }
   }
}

Essa classe é bem simples. No exemplo, é usado um banco de dados com uma tabela e uma coluna, mas, cobre todos os conceitos básicos de manipulação de BD do Android. Não entraremos em detalhes em relação a essa classe aqui, pois o próprio código é bem claro, mas algumas coisas são importantes de observar aqui:

  • inclui uma implementação de SQLiteOpenHelper como uma inner-classe.
  • demonstra duas formas diferentes de interagir com o banco de dados em código, com um SQLiteStatement para inserts  (que tem a vantagem de ser pré-compilado, que é mais fácil do que os metódos SQLiteDatabase.query() que você deve estar familiar), e diretamente pela chamada de selects.
  • mostra um padrão útil (de forma bem simplificada) de expor dados persistentes e metódos de recuperação do helper.

Para usar essa classe no Main.java padrão que nós permitimos que o Plug-In Android do Eclipse gerasse, nós precisamos modificar um pouco ela para criar uma instância de nossa classe DataHelper e então usa-la para criar e recuperar dados com visto abaixo. (NOTA: No mundo real, você deve fazer isso uma vez por aplicação, pelo uso da classe Android ‘Application’, onde você pode instânciar a DataHelper uma vez e então expor a sua referência em outras classes):

package com.totsp.androidexamples;

im<wbr>port android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

impo<wbr>rt java.util.List;

public class Main extends Activity {

   private TextView output;

   private DataHelper dh;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceSt<wbr>ate);
        setContentView(R.layout.main);<wbr>

        this.output = (TextView) this.findViewById(R.id.out_tex<wbr>t);

        this.dh = new DataHelper(this);
        this.dh.deleteAll();
        this.dh.insert("Porky Pig");
        this.dh.insert("Foghorn Leghorn");
        this.dh.insert("Yosemite Sam");
        List<String> names = this.dh.selectAll();
        StringBuilder sb = new StringBuilder();
        sb.append("Names in database:\n");
        for (String name : names) {
           sb.append(name + "\n");
        }

        Log.d("EXAMPLE", "names size - " + names.size());

        this.output.setText(sb.toStrin<wbr>g());

    }
}

Para essa classe funcionar, nós também precisamos mudar o arquivo de layout main.xml no qual a aplicação se baseia. Precisamos incluir algumas TextView adicionais para exibir a saída, como descrito no código abaixo:

<?xml version="1.0" encoding="utf-8"?>
<Scro<wbr>llView
  xmlns:android="http://schemas.<wbr>android.com/apk/res/android"
      android:layout_width="fill_par<wbr>ent"
      android:layout_height="wrap_co<wbr>ntent">
  <LinearLayout xmlns:android=
     "http://schemas.android.com/ap<wbr>k/res/android"
      android:orientation="vertical"<wbr>
      android:layout_width="fill_par<wbr>ent"
      android:layout_height="fill_pa<wbr>rent">
    <TextView android:layout_width="fill_par<wbr>ent"
       android:layout_height="wrap_co<wbr>ntent"
       android:text="@string/hello" />
    <TextView android:id="@+id/out_text"
       android:layout_width="fill_par<wbr>ent"
       android:layout_height="wrap_co<wbr>ntent"
       android:text="" />
  </LinearLayout>
</Scr<wbr>ollView>

Com isso, nós temos uma aplicação que deve ser capaz de ser carregada e executada pelo emulador, e devemos ver a primeira tela como vista acima – uma tela preta básica como texto branco com alguns nomes na saída. Os nomes vem do banco de dados, que agora existe, e podemos passar agora para o uso do comando sqlite3.

O Android usa o SQLite como banco de dados embutido. Se você precisa armazenar dados da aplicação localmente, ao invés de simplementes usar arquivos ou meios mais complicados como armazenamento via rede, você pode usar banco de dados.

Para examinar o banco de dados nós podemos usar o shell fornecido pelo Android Debug Bridge. Para usa-lo precisamos da pasta “tools” do SDK em nosso path. Se iniciamos a aplicação no Eclipse (Run As->Android Application), e deixarmos ela rodando, seremos capazes de logar com o comando:

ccollins@crotalus:~$ adb -e shell
#

A opção -e diz ao ADB para usar o emulador, ao invés de um possivel dispositivo que esteja conectado (e retorna um erro de mais de um emulador estiver sendo executado).  O “#’ é o prompt, que diz que estamos logados.

Assim que estivermos logados podemos navegar com o comando “ls”. O diretório que estamos interessados é /data/data/com.totsp.androidexamples/databases (cada aplicação tem um diretório específico). Podemos mudar para essa pasta com o comando “cd”. Entrando nessa pasta, podemos usar o comando sqlite3 para examinar nosso banco de dados, como mostrado:

# sqlite3 example.db
SQLite version 3.5.9
Enter ".help" for instructions
sqlite> select * from sqlite_master;
table|android_metadata|android_metadata|3|CREATE TABLE android_metadata (locale TEXT)
table|table1|table1|4|CREATE TABLE table1 (id INTEGER PRIMARY KEY, name TEXT)
sqlite>

Após logarmos com o comando “sqlite3 [database_name]” rodamos os comando SQL que são suportados pelo SQLite. Uma das tabelas importantes é sqlite_master, onde podemos ver todas as tabelas de nossa base de dados.

Alguns comandos interessantes:

sqlite> .schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE table1 (id INTEGER PRIMARY KEY, name TEXT);
sqlite> .tables
android_metadata  table1
sqlite> select * from table1;
1|Porky Pig
2|Foghorn Leghorn
3|Yosemite Sam
sqlite>

Podemos usar .help dentro do shell do sqlite3 para ver uma lista completa de comandos. Comandos que começam com um ponto são internos ao SQLite, e executam uma função –  como .schema e .tables como visto acima. Comando SQL podem ser executados diretamente digitando eles na linha de comando.

Com isso, temos os conceitos básicos. Temos uma aplicação que cria um banco de dados e armazena e recupera dados dele, e entramos um pouco nos metódos de exploração da SQLite através do ADB.  Como exercicio futuro, você pode expandir o conhecimento adquirido aqui pesquisando sobre outras tabelas envolvidas, assim como exemplos com unidades de teste do Android.

Traduzido de http://www.screaming-penguin.com

  • Alex

    Malandrão você hein? Pegando o tutorial de outro cara e colocando como se fosse seu.
    Pra quem quiser ver o tutorial original:
    http://www.screaming-penguin.com/node/7742

    • Você sabe inglês? bom pra você. Essa tradução foi feita para quem não sabe. A propósito, o link já consta no final do artigo.

  • Ricardo Costa

    Valeu Kleber ficou muito bom, acessei dum smartyfone e deu pra ler de boa.

  • Jo

    Nos tutos que li, percebi que no onCreate (de SQLiteOpenHelper ) são criadas as tabelas. É sempre assim? A criação das tabelas deve ficar no código?
    Quando esse método onCreate é executado?

    • klebermo

      Jo, Boa noite.
      Em minha opinião, essa é a forma de garantir que existam no banco de dados as tabelas que serão manipuladas pelo sistema (o método onCreate é executado quando a Activity é iniciada). Acho que se você puder garantir isso, pode colocar esse código em outro local.

  • Eleandro

    Boa tarde, atualmente tenho uma aplicação com BD toda feita em Access, quero migrar parte dela para rodar em um tablet Android. Quero saber se você faz esse serviço.

    Grato.

    • klebermo

      Eleandro, como vai?
      Poderia me mandar imagens das telas dos formulários e do modelo do banco de dados para que eu possa avaliar a complexidade do projeto e calcular um orçamento?
      Mande para o e-mail: contato@klebermota.eti.br.
      Atenciosamente,
      Kleber Mota

  • marciomendezs

    Cara, vou explicar uma coisa: Quando se faz um tutorial básico, deve-se detalhar muito bem, porque quem já sabe acha-o muito básico e quem ainda não sabe tem muitas dificuldades. Na primeira classe (DataHelper) que é falada para criar, você não explica aonde ela deve ser criada, no eclipse tem várias pastas do projeto, em qual delas deve ser criada? De novo: quem ainda não sabe tem estas dúvidas e é para isto que procura um tutorial básico. No link do projeto completo não funciona.
    Faz um favor pra nação: quando fizer um tutorial básico, explique bem para não deixar dúvidas para um iniciante.
    Este tutorial, apesar de traduzido e não colocado o endereço completo, faltam muitas coisas para facilitar o aprendizado, coisas que o tradutor poderia melhorar. Apesar dos pesares, valeu a intenção.

    • klebermo

      Marcio,
      eu traduzi mais de um tutorial justamente para complementar informações que podem estar faltando em um deles. Além do mais, tento responder todas as dúvidas que chegam através dos comentários (que tento responder todos que chegam aqui neste blog) de acordo com meu conhecimento atual; praticamente todos os artigos postados aqui servem como aprendizado pra mim também.
      Espero ter esclarecido qualquer dúvida,
      Kleber Mota
      ps.: em tempo, o link para o artigo original está no final do artigo, e é o endereço completo.

  • Giovanna_

    Olá cleber, eu realmente queria saber onde a classe DataHelper deve ser criada, se puder ajuda obg!

    • Giovana, normalmente, para projetos pequenos, pode ser criada no mesmo pacote onde está a Activity onde ela é chamada. Para projetos maiores, provavelmente será criada em um pacote separado, junto com outras classes auxiliares.

  • Giovanna_

    eu achei que fosse lá mesmo, obrigada kleber!