Android
Regno
Ricerca…
introduzione
Realm Mobile Database è un'alternativa a SQLite. Il Realm Mobile Database è molto più veloce di un ORM e spesso più veloce di SQLite non elaborato.
Benefici
Funzionalità offline, query veloci, threading sicuro, app multipiattaforma, crittografia, architettura reattiva.
Osservazioni
Quando si utilizza Realm, è necessario ricordare che non è necessario passare istanze RealmObjects, RealmResults e Realm tra i thread. Se hai bisogno di una query su un determinato thread, apri un'istanza di Realm su quel thread. Alla fine del thread, dovresti chiudere il Realm.
NOTA LEGALE : l'utente comprende che il Software potrebbe contenere funzioni crittografiche che potrebbero essere soggette a restrizioni sull'esportazione e dichiara e garantisce di non trovarsi in un Paese soggetto a restrizioni o embargo sulle esportazioni degli Stati Uniti, tra cui Cuba, Iran, Nord Corea, Sudan, Siria o regione della Crimea e che non si trovi nell'elenco del Dipartimento del commercio di persone negate, parti non verificate o affiliate a un'entità soggetta a restrizioni.
Aggiunta di regno al tuo progetto
Aggiungi la seguente dipendenza al file build.gradle
livello di progetto .
dependencies {
classpath "io.realm:realm-gradle-plugin:3.1.2"
}
Aggiungi il seguente diritto nella parte superiore del tuo file build.gradle
livello di app .
apply plugin: 'realm-android'
Completa una sincronizzazione gradle e ora hai Realm aggiunto come dipendenza al tuo progetto!
Il reame richiede una chiamata iniziale dalla 2.0.0 prima di usarlo. Puoi farlo nella classe Application
o nel metodo onCreate
della tua prima attività.
Realm.init(this); // added in Realm 2.0.0
Realm.setDefaultConfiguration(new RealmConfiguration.Builder().build());
Modelli di regno
I modelli di realm devono estendere la classe base RealmObject
, definiscono lo schema del database sottostante.
I tipi di campo supportati sono boolean
, byte
, short
, int
, long
, float
, double
, String
, Date
, byte[]
, collegamenti ad altri RealmObject
RealmList<T extends RealmModel>
e 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
}
Se aggiungi (o rimuovi) un nuovo campo a RealmObject (o aggiungi una nuova classe RealmObject o ne elimini uno esistente), sarà necessaria una migrazione . È possibile impostare deleteIfMigrationNeeded()
in RealmConfiguration.Builder
o definire la migrazione necessaria. La migrazione è necessaria anche quando si aggiunge (o si rimuove) @Required
, o @Index
o @PrimaryKey
annotazione @PrimaryKey
.
Le relazioni devono essere impostate manualmente, NON sono automatiche in base alle chiavi primarie.
Dalla versione 0.88.0, è anche possibile utilizzare campi pubblici anziché campi privati / getter / setter nelle classi RealmObject.
È anche possibile implementare RealmModel
invece di estendere RealmObject
, se la classe è anche annotata con @RealmClass
.
@RealmClass
public class Person implements RealmModel {
// ...
}
In tal caso, metodi come person.deleteFromRealm()
o person.addChangeListener()
vengono sostituiti con RealmObject.deleteFromRealm(person)
e RealmObject.addChangeListener(person)
.
Le limitazioni sono che da un RealmObject
, solo RealmObject
può essere esteso, e non c'è supporto per i campi final
, volatile
e transient
.
È importante che una classe di RealmObject gestita possa essere modificata solo in una transazione. Un RealmObject gestito non può essere passato tra i thread.
Elenco di primitive (RealmList )
Attualmente Realm non supporta la memorizzazione di un elenco di primitive. È nella loro lista delle cose da fare ( GitHub issue # 575 ), ma per il momento, ecco una soluzione alternativa.
Crea una nuova classe per il tuo tipo primitivo, questo usa Integer, ma cambialo per qualunque cosa tu voglia immagazzinare.
public class RealmInteger extends RealmObject {
private int val;
public RealmInteger() {
}
public RealmInteger(int val) {
this.val = val;
}
// Getters and setters
}
Ora puoi usare questo nel tuo RealmObject
.
public class MainObject extends RealmObject {
private String name;
private RealmList<RealmInteger> ints;
// Getters and setters
}
Se stai utilizzando GSON
per popolare RealmObject
, dovrai aggiungere un adattatore di tipo personalizzato.
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();
risorse try-with-
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
}
Le risorse Prova con possono essere utilizzate solo da KITKAT (minSDK 19)
Query ordinate
Per ordinare una query, invece di usare findAll()
, dovresti usare findAllSorted()
.
RealmResults<SomeObject> results = realm.where(SomeObject.class)
.findAllSorted("sortField", Sort.ASCENDING);
Nota:
sort()
restituisce un nuovo RealmResults che è ordinato, ma un aggiornamento a questo RealmResults lo ripristinerà. Se usi sort()
, dovresti sempre riordinarlo in RealmChangeListener
, rimuovere RealmChangeListener
dai precedenti RealmResults
e aggiungerlo ai nuovi RealmResults
restituiti. L'utilizzo di sort()
su un RealmResults
restituito da una query asincrona non ancora caricata avrà esito negativo.
findAllSorted()
restituirà sempre i risultati ordinati per il campo, anche se viene aggiornato. Si consiglia di utilizzare findAllSorted()
.
Query asincrone
Ogni metodo di ricerca sincrona (come findAll()
o findAllSorted()
) ha una controparte asincrona ( findAllAsync()
/ findAllSortedAsync()
).
Le query asincrone offload la valutazione di RealmResults
su un altro thread. Per ricevere questi risultati sul thread corrente, il thread corrente deve essere un thread looper (leggi: le query asincrone di solito funzionano solo sul thread dell'interfaccia utente).
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);
Usare Realm con RxJava
Per le query, Realm fornisce il metodo realmResults.asObservable()
. L'osservazione dei risultati è possibile solo sui thread looper (in genere il thread dell'interfaccia utente).
Perché ciò funzioni, la tua configurazione deve contenere quanto segue
realmConfiguration = new RealmConfiguration.Builder(context) //
.rxFactory(new RealmObservableFactory()) //
//...
.build();
Successivamente, è possibile utilizzare i risultati come osservabili.
Observable<RealmResults<SomeObject>> observable = results.asObservable();
Per le query asincrone, è necessario filtrare i risultati per isLoaded()
, in modo da ricevere un evento solo quando la query è stata eseguita. Questo filter()
non è necessario per le query sincrone ( isLoaded()
restituisce sempre true
nelle query di sincronizzazione).
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));
Per le scritture, è necessario utilizzare il metodo executeTransactionAsync()
o aprire un'istanza di Realm sul thread in background, eseguire la transazione in modo sincrono, quindi chiudere l'istanza di 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 di base
Impostazione di un'istanza
Per usare Realm devi prima ottenere un'istanza di esso. Ogni istanza di Realm viene mappata su un file su disco. Il modo più semplice per ottenere un'istanza è il seguente:
// 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();
Il metodo Realm.getInstance()
crea il file di database se non è stato creato, altrimenti apre il file. L'oggetto RealmConfiguration
controlla tutti gli aspetti di come viene creato un Reame, sia che si tratti di un database inMemory()
, nome del file Realm, se il Reame deve essere cancellato se è necessaria una migrazione, i dati iniziali, ecc.
Si noti che le chiamate a Realm.getInstance()
sono conteggiate con riferimento (ogni chiamata incrementa un contatore) e il contatore viene decrementato quando viene chiamato realm.close()
.
Chiusura di un'istanza
Nei thread in background, è molto importante chiudere le istanze di Realm quando non vengono più utilizzate (ad esempio, la transazione è completa e l'esecuzione del thread termina). La mancata chiusura di tutte le istanze di Realm sul thread in background determina il blocco della versione e può causare una notevole crescita delle dimensioni del file.
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 notare che sopra il livello API 19, puoi sostituire questo codice con questo:
try(Realm realm = Realm.getDefaultInstance()) {
// ...
}
Modelli
Il prossimo passo sarebbe creare i tuoi modelli. Qui potrebbe essere posta una domanda, "che cos'è un modello?". Un modello è una struttura che definisce le proprietà di un oggetto che viene memorizzato nel database. Ad esempio, nel seguito modelliamo 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;
}
}
Nota che i tuoi modelli dovrebbero estendere la classe RealmObject. La chiave primaria viene inoltre specificata @PrimaryKey
. Le chiavi primarie possono essere nulle, ma solo un elemento può avere null
come chiave primaria. Inoltre puoi usare l'annotazione @Ignore
per i campi che non dovrebbero essere persistenti sul disco:
@Ignore
private String isbn;
Inserimento o aggiornamento dei dati
Per poter archiviare un oggetto libro nell'istanza del database di Realm, è possibile prima creare un'istanza del modello e quindi memorizzarla nel database tramite il metodo copyToRealm
. Per la creazione o l'aggiornamento è possibile utilizzare copyToRealmOrUpdate
. (Un'alternativa più veloce è l' insertOrUpdate()
appena aggiunto.
// 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);
}
});
Si noti che tutte le modifiche ai dati devono avvenire in una transazione. Un altro modo per creare un oggetto è utilizzare il seguente modello:
Book book = realm.createObject(Book.class, primaryKey);
...
Interrogare il database
Tutti i libri:
RealmResults<Book> results = realm.where(Book.class).findAll();
Tutti i libri con ID superiore a 10:
RealmResults<Book> results = realm.where(Book.class) .greaterThan("id", 10) .findAll();
Libri di
'Taylor Swift'
o'%Peter%'
:RealmResults<Book> results = realm.where(Book.class) .beginGroup() .equalTo("author", "Taylor Swift") .or() .contains("author", "Peter") .endGroup().findAll();
Cancellare un oggetto
Ad esempio, vogliamo eliminare tutti i libri di 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();
}
});