Android
Reino
Buscar..
Introducción
Realm Mobile Database es una alternativa a SQLite. Realm Mobile Database es mucho más rápido que un ORM y, a menudo, más rápido que SQLite sin procesar.
Beneficios
Funcionalidad fuera de línea, consultas rápidas, subprocesos seguros, aplicaciones multiplataforma, cifrado, arquitectura reactiva.
Observaciones
Cuando use Realm, debe recordar que no debe pasar las instancias RealmObjects, RealmResults y Realm entre los subprocesos. Si necesita una consulta en un hilo determinado, abra una instancia de Realm en ese hilo. Al terminar el hilo, debes cerrar el Reino.
NOTA LEGAL : Usted comprende que el Software puede contener funciones criptográficas que pueden estar sujetas a restricciones a la exportación, y usted declara y garantiza que no se encuentra en un país sujeto a restricciones o embargo de exportaciones de Estados Unidos, incluidos Cuba, Irán, Norte. Corea, Sudán, Siria o la región de Crimea , y que usted no está en la lista del Departamento de Comercio de Personas Negadas, Partes no verificadas, o afiliado a una Entidad Restringida.
Agregando Realm a tu proyecto
Agregue la siguiente dependencia a su archivo build.gradle
nivel de proyecto .
dependencies {
classpath "io.realm:realm-gradle-plugin:3.1.2"
}
Agregue el siguiente derecho en la parte superior del archivo build.gradle
de su nivel de aplicación .
apply plugin: 'realm-android'
¡Complete una sincronización de Gradle y ahora tiene Realm agregado como una dependencia para su proyecto!
Realm requiere una llamada inicial desde 2.0.0 antes de usarla. Puede hacer esto en su clase de Application
o en el método onCreate
su primera Actividad.
Realm.init(this); // added in Realm 2.0.0
Realm.setDefaultConfiguration(new RealmConfiguration.Builder().build());
Modelos de reino
Los modelos de reino deben extender la clase base RealmObject
, definen el esquema de la base de datos subyacente.
Los tipos de campo admitidos son boolean
, byte
, short
, int
, long
, float
, double
, String
, Date
, byte[]
, enlaces a otros RealmObject
s, y RealmList<T extends RealmModel>
.
public class Person extends RealmObject {
@PrimaryKey //primary key is also implicitly an @Index
//it is required for `copyToRealmOrUpdate()` to update the object.
private long id;
@Index //index makes queries faster on this field
@Required //prevents `null` value from being inserted
private String name;
private RealmList<Dog> dogs; //->many relationship to Dog
private Person spouse; //->one relationship to Person
@Ignore
private Calendar birthday; //calendars are not supported but can be ignored
// getters, setters
}
Si agrega (o elimina) un nuevo campo a su RealmObject (o agrega una nueva clase de RealmObject o elimina una existente), se necesitará una migración . Puede establecer deleteIfMigrationNeeded()
en su RealmConfiguration.Builder
, o definir la migración necesaria. La migración también es necesaria al agregar (o eliminar) @Required
, o @Index
, o @PrimaryKey
anotación.
Las relaciones deben establecerse manualmente, NO son automáticas basadas en claves primarias.
Desde 0.88.0, también es posible usar campos públicos en lugar de campos / getters / setters privados en las clases de RealmObject.
También es posible implementar RealmModel
lugar de extender RealmObject
, si la clase también se anota con @RealmClass
.
@RealmClass
public class Person implements RealmModel {
// ...
}
En ese caso, los métodos como person.deleteFromRealm()
o person.addChangeListener()
se reemplazan con RealmObject.deleteFromRealm(person)
y RealmObject.addChangeListener(person)
.
Las limitaciones son que, mediante un objeto RealmObject
, solo se puede extender RealmObject
, y no hay soporte para transient
campos final
, volatile
y transient
.
Es importante que una clase administrada de RealmObject solo pueda modificarse en una transacción. Un RealmObject administrado no se puede pasar entre hilos.
Lista de primitivas (RealmList )
El reino actualmente no admite el almacenamiento de una lista de primitivas. Está en su lista de tareas pendientes ( problema GitHub # 575 ), pero mientras tanto, aquí hay una solución.
Cree una nueva clase para su tipo primitivo, esto usa Integer, pero cámbielo por lo que quiera almacenar.
public class RealmInteger extends RealmObject {
private int val;
public RealmInteger() {
}
public RealmInteger(int val) {
this.val = val;
}
// Getters and setters
}
Ahora puedes usar esto en tu RealmObject
.
public class MainObject extends RealmObject {
private String name;
private RealmList<RealmInteger> ints;
// Getters and setters
}
Si está utilizando GSON
para completar su RealmObject
, deberá agregar un adaptador de tipo personalizado.
Type token = new TypeToken<RealmList<RealmInteger>>(){}.getType();
Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class);
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
})
.registerTypeAdapter(token, new TypeAdapter<RealmList<RealmInteger>>() {
@Override
public void write(JsonWriter out, RealmList<RealmInteger> value) throws IOException {
// Empty
}
@Override
public RealmList<RealmInteger> read(JsonReader in) throws IOException {
RealmList<RealmInteger> list = new RealmList<RealmInteger>();
in.beginArray();
while (in.hasNext()) {
list.add(new RealmInteger(in.nextInt()));
}
in.endArray();
return list;
}
})
.create();
probar con recursos
try (Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
//whatever Transaction that has to be done
}
});
//No need to close realm in try-with-resources
}
El intento con recursos solo se puede usar desde KITKAT (minSDK 19)
Consultas ordenadas
Para ordenar una consulta, en lugar de usar findAll()
, debe usar findAllSorted()
.
RealmResults<SomeObject> results = realm.where(SomeObject.class)
.findAllSorted("sortField", Sort.ASCENDING);
Nota:
sort()
devuelve un RealmResults completamente nuevo que está ordenado, pero una actualización de este RealmResults lo restablecerá. Si usa sort()
, siempre debe reorganizarlo en su RealmChangeListener
, eliminar el RealmChangeListener
de los RealmResults
anteriores y agregarlo a los nuevos RealmResults
. El uso de sort()
en un RealmResults
devuelto por una consulta asíncrona que aún no se ha cargado fallará.
findAllSorted()
siempre devolverá los resultados ordenados por campo, incluso si se actualiza. Se recomienda utilizar findAllSorted()
.
Consultas asincrónicas
Cada método de consulta síncrona (como findAll()
o findAllSorted()
) tiene una contraparte asíncrona ( findAllAsync()
/ findAllSortedAsync()
).
Las consultas asíncronas descargan la evaluación de RealmResults
a otro hilo. Para recibir estos resultados en el subproceso actual, el subproceso actual debe ser un subproceso looper (lea: las consultas asíncronas generalmente solo funcionan en el subproceso de la interfaz de usuario).
RealmChangeListener<RealmResults<SomeObject>> realmChangeListener; // field variable
realmChangeListener = new RealmChangeListener<RealmResults<SomeObject>>() {
@Override
public void onChange(RealmResults<SomeObject> element) {
// asyncResults are now loaded
adapter.updateData(element);
}
};
RealmResults<SomeObject> asyncResults = realm.where(SomeObject.class).findAllAsync();
asyncResults.addChangeListener(realmChangeListener);
Usando Realm con RxJava
Para consultas, Realm proporciona el método realmResults.asObservable()
. La observación de resultados solo es posible en los subprocesos del looper (normalmente el subproceso de la interfaz de usuario).
Para que esto funcione, su configuración debe contener lo siguiente
realmConfiguration = new RealmConfiguration.Builder(context) //
.rxFactory(new RealmObservableFactory()) //
//...
.build();
Después, puede utilizar sus resultados como un observable.
Observable<RealmResults<SomeObject>> observable = results.asObservable();
Para consultas asíncronas, debe filtrar los resultados por isLoaded()
, de modo que reciba un evento solo cuando la consulta se haya ejecutado. Este filter()
no es necesario para consultas síncronas ( isLoaded()
siempre devuelve true
en consultas de sincronización).
Subscription subscription = RxTextView.textChanges(editText).switchMap(charSequence ->
realm.where(SomeObject.class)
.contains("searchField", charSequence.toString(), Case.INSENSITIVE)
.findAllAsync()
.asObservable())
.filter(RealmResults::isLoaded) //
.subscribe(objects -> adapter.updateData(objects));
Para las escrituras, debe usar el método executeTransactionAsync()
o abrir una instancia de Realm en el subproceso en segundo plano, ejecutar la transacción de forma sincrónica y luego cerrar la instancia de Realm.
public Subscription loadObjectsFromNetwork() {
return objectApi.getObjects()
.subscribeOn(Schedulers.io())
.subscribe(response -> {
try(Realm realmInstance = Realm.getDefaultInstance()) {
realmInstance.executeTransaction(realm -> realm.insertOrUpdate(response.objects));
}
});
}
Uso básico
Configurando una instancia
Para usar Realm primero necesitas obtener una instancia de él. Cada instancia de Realm se asigna a un archivo en el disco. La forma más básica de obtener una instancia es la siguiente:
// Create configuration
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(context).build();
// Obtain realm instance
Realm realm = Realm.getInstance(realmConfiguration);
// or
Realm.setDefaultConfiguration(realmConfiguration);
Realm realm = Realm.getDefaultInstance();
El método Realm.getInstance()
crea el archivo de base de datos si no se ha creado; de lo contrario, abre el archivo. El objeto RealmConfiguration
controla todos los aspectos de cómo se crea un Reino, ya sea una base de datos en inMemory()
, el nombre del archivo del Reino, si se debe borrar el Reino si se necesita una migración, datos iniciales, etc.
Tenga en cuenta que las llamadas a Realm.getInstance()
se cuentan como referencia (cada llamada incrementa un contador), y el contador disminuye cuando se llama a realm.close()
.
Cerrando una instancia
En los subprocesos en segundo plano, es muy importante cerrar las instancias de Realm una vez que ya no se usan (por ejemplo, la transacción está completa y la ejecución del subproceso finaliza). Si no se cierran todas las instancias de Realm en el subproceso en segundo plano, la versión se anclará y esto puede causar un gran aumento en el tamaño del archivo.
Runnable runnable = new Runnable() {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
// ...
} finally {
if(realm != null) {
realm.close();
}
}
};
new Thread(runnable).start(); // background thread, like `doInBackground()` of AsyncTask
Vale la pena señalar que, por encima del nivel de API 19, puede reemplazar este código con solo esto:
try(Realm realm = Realm.getDefaultInstance()) {
// ...
}
Modelos
El siguiente paso sería crear tus modelos. Aquí se puede hacer una pregunta, "¿qué es un modelo?". Un modelo es una estructura que define las propiedades de un objeto que se almacena en la base de datos. Por ejemplo, en lo siguiente modelamos un libro.
public class Book extends RealmObject {
// Primary key of this entity
@PrimaryKey
private long id;
private String title;
@Index // faster queries
private String author;
// Standard getters & setter
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
Tenga en cuenta que sus modelos deben extender la clase RealmObject. La clave principal también se especifica mediante la anotación @PrimaryKey
. Las claves principales pueden ser nulas, pero solo un elemento puede tener un null
como clave principal. También puede usar la anotación @Ignore
para los campos que no deben persistir en el disco:
@Ignore
private String isbn;
Inserción o actualización de datos.
Para almacenar un objeto de libro en su instancia de la base de datos Realm, primero puede crear una instancia de su modelo y luego almacenarla en la base de datos a través del método copyToRealm
. Para crear o actualizar puede usar copyToRealmOrUpdate
. (Una alternativa más rápida es el añadido insertOrUpdate()
).
// Creating an instance of the model
Book book = new Book();
book.setId(1);
book.setTitle("Walking on air");
book.setAuthor("Taylor Swift")
// Store to the database
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.insertOrUpdate(book);
}
});
Tenga en cuenta que todos los cambios en los datos deben ocurrir en una transacción. Otra forma de crear un objeto es mediante el siguiente patrón:
Book book = realm.createObject(Book.class, primaryKey);
...
Consultar la base de datos
Todos los libros
RealmResults<Book> results = realm.where(Book.class).findAll();
Todos los libros que tengan una identificación superior a 10:
RealmResults<Book> results = realm.where(Book.class) .greaterThan("id", 10) .findAll();
Libros de
'Taylor Swift'
o'%Peter%'
:RealmResults<Book> results = realm.where(Book.class) .beginGroup() .equalTo("author", "Taylor Swift") .or() .contains("author", "Peter") .endGroup().findAll();
Borrando un objeto
Por ejemplo, queremos eliminar todos los libros de Taylor Swift:
// Start of transaction
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
// First Step: Query all Taylor Swift books
RealmResults<Book> results = ...
// Second Step: Delete elements in Realm
results.deleteAllFromRealm();
}
});