Utilizando SQLite e SharedPreferences em Aplicações Android

Este artigo explora o desenvolvimento de uma aplicação Android simples que utiliza SQLite para persistência de dados local, SharedPreferences para gerenciamento de sessão e um padrão para gerir a pilha de Activities.

Arquitetura Geral e Camadas de Dados

A aplicação segue uma estrutura comum: Activitys (UI), uma camada de Acesso a Dados (DAOs), uma clase auxiliar para o banco de dados (DbHelper) e classes de modelo (Entidades).

Gerenciamento do Banco de Dados com DbHelper

A classe DbHelper estende SQLiteOpenHelper, sendo responsável pela criação e atualização do esquema do banco de dados. Ela implementa um padrão Singleton para garantir uma única instância.


public class DatabaseHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "app_data.db";
    private static final int DB_VERSION = 1;
    private static DatabaseHelper sInstance;

    public static synchronized DatabaseHelper getInstance(Context ctx) {
        if (sInstance == null) {
            sInstance = new DatabaseHelper(ctx.getApplicationContext());
        }
        return sInstance;
    }

    private DatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS users (" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "username TEXT UNIQUE NOT NULL, " +
                "password_hash TEXT NOT NULL)");
        // Outras tabelas (ex: products, orders) seriam criadas aqui.
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // Lógica de migração entre versões do banco.
        onCreate(db);
    }
}

Camada de Acesso a Dados (DAO)

Os DAOs encapsulam a lógica de acesso ao banco de dados. Utilizam ContentValues para inserções e atualizações, e Cursor para consultas.


public class ProductDao {
    private final SQLiteDatabase database;

    public ProductDao(Context context) {
        DatabaseHelper helper = DatabaseHelper.getInstance(context);
        this.database = helper.getReadableDatabase();
    }

    public long insert(Product product) {
        ContentValues insertValues = new ContentValues();
        insertValues.put("name", product.getName());
        insertValues.put("price", product.getPrice());
        return database.insert("products", null, insertValues);
    }

    public Cursor fetchAll() {
        return database.query("products", null, null, null, null, null, "name ASC");
    }
}

Utilizando SimpleCursorAdapter para Exibir Dados

Para exibir dados de um Cursor em uma ListView, o SimpleCursorAdapter mapeia colunas do banco para Views do layout.


// Em uma Activity ou Fragment
Cursor dataCursor = productDao.fetchAll();
String[] fromColumns = {"name", "price"};
int[] toViews = {R.id.tvProductName, R.id.tvProductPrice};

SimpleCursorAdapter adapter = new SimpleCursorAdapter(
        this,
        R.layout.list_item_product,
        dataCursor,
        fromColumns,
        toViews,
        0); // Flags como FLAG_REGISTER_CONTENT_OBSERVER podem ser usadas
listView.setAdapter(adapter);

Gerenciamento de Sessão com SharedPreferences

O SharedPreferences é ideal para salvar dados simples de configuração, como o estado de login. Um arquivo (ex: user_session.xml) armazena pares chave-valor.


// Ao fazer login com sucesso
SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("LOGGED_IN_USER", "usuario_exemplo");
editor.putBoolean("IS_LOGGED_IN", true);
editor.apply(); // Usar apply() para gravação assíncrona

// Em outra Activity para verificar o login
SharedPreferences prefs = getSharedPreferences("user_session", MODE_PRIVATE);
boolean isLoggedIn = prefs.getBoolean("IS_LOGGED_IN", false);
if (!isLoggedIn) {
    // Redirecionar para a tela de login
}

Gerenciamento da Pilha de Activities (Activity Stack)

Uma classe Application personalizada pode manter uma lista de Activities ativas para facilitar o encerramento controlado da aplicação.


public class App extends Application {
    private static App instance;
    private final List<activity> activeActivities = new ArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
    }

    public static App getInstance() {
        return instance;
    }

    public void registerActivity(Activity activity) {
        activeActivities.add(activity);
    }

    public void unregisterActivity(Activity activity) {
        activeActivities.remove(activity);
    }

    public void exitApplication() {
        for (Activity activity : activeActivities) {
            if (!activity.isFinishing()) {
                activity.finish();
            }
        }
        activeActivities.clear();
        System.exit(0);
    }
}

// Em cada Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    App.getInstance().registerActivity(this);
    // ...
}

@Override
protected void onDestroy() {
    super.onDestroy();
    App.getInstance().unregisterActivity(this);
}
</activity>

Operações CRUD Diretas com SQLiteDatabase

Também é possível executar operações CRUD diretamente usando os métodos da classe SQLiteDatabase.


SQLiteDatabase db = DatabaseHelper.getInstance(this).getWritableDatabase();

// INSERT
ContentValues values = new ContentValues();
values.put("name", "Novo Produto");
values.put("price", 19.99);
long newRowId = db.insert("products", null, values);

// UPDATE
ContentValues updateValues = new ContentValues();
updateValues.put("price", 24.99);
int rowsAffected = db.update("products", updateValues, "name = ?", new String[]{"Novo Produto"});

// DELETE
int deletedRows = db.delete("products", "id = ?", new String[]{"1"});

// QUERY com rawQuery
Cursor cursor = db.rawQuery("SELECT * FROM products WHERE price > ?", new String[]{"20.00"});

Considerações de Design

O uso de DAOs abstrai os detalhes de implementação do banco de dados, tornando o código mais limpo e testável. O SharedPreferences deve ser usado apenas para dados leves, enquanto SQLite é indicado para dados estruturados e complexos. O gerenciamento da pilha de Activities via classe Application ajuda a controlar o ciclo de vida da aplicação e realizar limpeza de recursos.

Tags: sqlite android SharedPreferences SimpleCursorAdapter SQLiteDatabase

Publicado em 6-15 06:44 por Thomas