Iniciação ao Realm

Douglas Drumond
May 9, 2016 · 5 min read

Inevitavelmente, exceto nos aplicativos extremamente triviais e que geralmente só servem de exemplo, é necessária alguma forma de persistência de dados no aparelho do usuário. No Android temos diversas formas de persistir dados. A mais simples é o SharedPreferences, que nada mais é que um arquivo contendo chave e valor. Também é possível escrever e ler de arquivos no sistema de arquivos. Mas para grande quantidade de dados estruturados, a saída é banco de dados.

SQLite vs ORM vs Realm

O banco de dados padrão do Android é o SQLite. SQLite é um sistema gerenciador de banco de dados relacional e autocontido. Ou seja, não é necessário um complexo sistema cliente-servidor para funcionar, o que o torna uma ótima escolha para embarcados. Mas, o SQLite no Android exige muito código boilerplate e a terrível mistura de SQL em strings no meio de código Java. Isso faz com que os desenvolvedores tenham que escolher entre repetir nomes de tabelas e campos em vários lugares (o que é propenso a erros) ou colocar esses nomes em constantes e conviver com uma quantidade grande de concatenação de texto, tornando a leitura desagradável.

Para suprir essa deficiência, diversos frameworks de ORM foram desenvolvidos para Android, como GreenDAO ou OrmLite.

Um ORM, de object-relational mapping, é uma camada que converte entre objetos, usados em alto nível, e as tabelas de um banco de dados relacional. Com isso, o programador lida apenas com classes e objetos já conhecidos da programação orientada a objetos.

No entanto, um ORM adiciona uma camada de processamento extra e o desempenho nem sempre é aceitável. Além, claro, de aumentar complexidade do código.

Por fim, ainda há uma outra alternativa de banco de dados no Android, o Realm.

O que é o Realm?

Devido à sintaxe similar a de ORMs, algumas pessoas pensam que Realm é um ORM para SQLite. Realm não é um ORM, é um banco de dados orientado a objetos criado especificamente para mobile. É um substituto para SQLite no Android e Core Data no iOS. O núcleo foi feito em C++ e os benchmarks indicam performance até 20x superior ao SQLite em algumas operações.

Adicionando o Realm

Para usar o Realm no Android, seguimos o caminho tradicional, ou seja, via Gradle. No arquivo build.gradle no nível superior, adicione as seguintes linhas:

buildscript {
repositories {
jcenter()
}
dependencies {

classpath 'io.realm:realm-gradle-plugin:1.0.0'

}
}

E no build.gradle do módulo da aplicação, adicione:

apply plugin: 'realm-android'

Por exemplo:

apply plugin: 'com.android.application'

apply plugin: 'realm-android'

android {
compileSdkVersion 23
buildToolsVersion "23.0.3"

defaultConfig {
applicationId "com.cafelinear.realmsample"
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
}

Só isso. Seu aplicativo está pronto para usar o Realm.

Definindo tabelas

No Realm, não dizemos realmente tabelas, são modelos (Models), mas o comportamento é semelhante ao das tabelas. Para criar um Model, basta estender a classe RealmObject. Por exemplo:

public class TaskModel extends RealmObject {

private int taskId;
private String description;
private boolean checked;

public int getTaskId() {
return taskId;
}

public void setTaskId(final int taskId) {
this.taskId = taskId;
}

public String getDescription() {
return description;
}

public void setDescription(final String description) {
this.description = description;
}

public boolean isChecked() {
return checked;
}

public void setChecked(final boolean checked) {
this.checked = checked;
}
}

Crie getters e setters para seus campos. (N.A.: na versão anterior, afirmei que não poderia haver código customizado nos getters e setters, mas desde a versão 0.88 isso tornou-se falso. Obrigado, @chrmelchior pelo aviso.)

Construtores parametrizados podem ser adicionados, se desejar, mas o construtor default deve estar presente.

Além dos tipos primitivos (boolean, byte, short, int, long, float, double), Realm também suporta String, Date e byte[]. Também podem ser usadas as classes equivalentes aos tipos primitivos (Boolean, Byte, etc), para caso algum campo seja nulo. Em caso de relacionamento entre models, é permitido criar campos do tipo RealmObject and RealmList<? extends RealmObject>. Para os tipos numéricos inteiros (byte, short, int e long), internamente Realm irá utilizar long, mas convenientemente suporta todos os quatro tipos no modelo.

Também podemos fazer uso de certas anotações para indicar características especiais nos campos:

  • @Required: campo não pode ser nulo;
  • @Index: campo será indexado;
  • @PrimaryKey chave primária;
  • e, por fim, @Ignore para campos não persistidos em disco.

Alternativa

Em vez de estender a classe RealmObject, uma alternativa é implementar a interface RealmModel. Essa opção é recente, foi adicionada na versão 0.89. O jeito recomendado ainda é estendendo RealmObject. Ao implementar RealmModel, também devemos adicionar a anotação @RealmClass, pois herança de anotações de uma interface ainda não é suportado no Android.

No exemplo acima, teríamos:

@RealmClass
public class
TaskModel implements RealmModel {

}

Essa interface não contém métodos, não há nada a ser de fato implementado.

Acessando o banco de dados

Agora que temos um Model, como proceder para efetivamente usar o banco de dados?

O primeiro passo para efetuar qualquer operação no Realm é obter uma instância de RealmConfiguration para, em seguida, obter a instância do banco. A partir dessa instância do banco, podemos finalmente efetuar buscas, inserir dados, etc., e a sintaxe é semelhante a de ORMs.

RealmConfiguration realmConfig = new RealmConfiguration                  
.Builder(context).build();
Realm realm = Realm.getInstance(realmConfig);

Pronto, agora todas as operações no banco serão feitas a partir do objeto realm.

Por exemplo, para listar todas as tarefas cadastradas, é possível usar findAll:

RealmResults<TaskModel> tasks =     
realm.where(TaskModel.class).findAll();

Se quiséssemos recuperar somente as tarefas já concluídas, poderíamos adicionar restrições:

tasks = realm.where(TaskModel.class)
.equalTo("checked", true).findAll();

Muito fácil, não?

Para inserir um dado, basta criar um objeto do tipo do Model e inseri-lo, colocando a chamada de inserção dentro de uma transação:

// Cria um novo objeto
TaskModel newTask = new TaskModel();
newTask.setTaskId(1); // Use um ID válido
newTask.setDescription("Hew task description");
newTask.setChecked(false);

// Insere no banco
realm.beginTransaction();
realm.copyToRealm(newTask);
realm.commitTransaction();

No caso de atualizações, após recuperar o objeto numa busca, bastaria alterar o campo desejado e efetuar a mesma transação de inserção. Por exemplo, vamos marcar a primeira tarefa:

tasks = realm.where(TaskModel.class)
.equalTo("checked", false).findAll();

TaskModel changedTask = tasks.get(0);
changedTask.setChecked(true);
realm.beginTransaction();
realm.copyToRealm(changedTask);
realm.commitTransaction();

Somente a exclusão é levemente diferente, feita a partir da instância do modelo:

changedTask.deleteFromRealm();

Caso tenha implementado a interface RealmModel, use o método estático em RealmObject:

RealmObject.deleteFromRealm(changedTask);

Muito mais simples que SQLite.

Também é possível executar operações assíncronas com executeTransactionAsync:

realm.executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realmDb) {
TaskModel taskModel = realmDb.createObject(TaskModel.class);
taskModel.setChecked(true);
taskModel.setDescription("Limpar o quarto");
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
// Transação foi um sucesso
}
}, new Realm.Transaction.OnError() {
@Override
public void onError(Throwable error) {
// Transação foi cancelada devido a algum erro.
}
});

Os listeners de OnSuccess() e OnError() são opcionais.

Conclusão

Esses são os primeiros passos para colocar o Realm em um aplicativo e vimos algumas de suas funcionalidades. Porém, ainda temos uma gama de outras funcionalidades interessantes e tópicos para abordarmos em um próximo artigo.

Experimente o Realm e será convencido da sua facilidade.

Android Dev BR

Artigos em português sobre Android, curados pela comunidade Android Dev BR. Junte-se a nós: slack.androiddevbr.org.

Douglas Drumond

Written by

Android developer and mathematician. Hobbyist photographer. GDG Campinas founder and organizer. Former maintainer of MacVim.

Android Dev BR

Artigos em português sobre Android, curados pela comunidade Android Dev BR. Junte-se a nós: slack.androiddevbr.org.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade