Android
Reich
Suche…
Einführung
Realm Mobile Database ist eine Alternative zu SQLite. Die Realm Mobile-Datenbank ist viel schneller als ein ORM und häufig auch schneller als das rohe SQLite.
Leistungen
Offline-Funktionalität, schnelle Abfragen, sicheres Threading, plattformübergreifende Apps, Verschlüsselung, reaktive Architektur.
Bemerkungen
Wenn Sie Realm verwenden, müssen Sie daran denken, dass Sie keine Instanzen von RealmObjects, RealmResults und Realm zwischen Threads übergeben dürfen. Wenn Sie eine Abfrage für einen bestimmten Thread benötigen, öffnen Sie eine Realm-Instanz in diesem Thread. Bei Beendigung des Threads sollten Sie das Realm schließen.
RECHTLICHER HINWEIS : Sie verstehen, dass die Software möglicherweise kryptografische Funktionen enthält, die Exportbeschränkungen unterliegen können, und Sie versichern und garantieren, dass Sie sich nicht in einem Land befinden , das Exportbeschränkungen oder Embargo der USA unterliegt, einschließlich Kuba, Iran, Nord Korea, Sudan, Syrien oder die Krimregion , und Sie befinden sich nicht auf der Liste der abgelehnten Personen, nicht verifizierten Parteien des Handelsministeriums oder einer eingeschränkten Einheit.
Realm zu Ihrem Projekt hinzufügen
Fügen Sie der Projektstufe build.gradle
die folgende Abhängigkeit build.gradle
.
dependencies {
classpath "io.realm:realm-gradle-plugin:3.1.2"
}
Fügen Sie oben in Ihrer build.gradle
Datei auf App- Ebene build.gradle
:
apply plugin: 'realm-android'
Schließen Sie eine Gradle-Synchronisierung ab, und Sie haben jetzt Realm als Abhängigkeit zu Ihrem Projekt hinzugefügt!
Realm erfordert vor der Verwendung einen ersten Aufruf seit 2.0.0. Sie können dies in Ihrer Application
Klasse oder in der onCreate
Methode Ihrer ersten Activity onCreate
.
Realm.init(this); // added in Realm 2.0.0
Realm.setDefaultConfiguration(new RealmConfiguration.Builder().build());
Realm-Modelle
Realm-Modelle müssen die RealmObject
Basisklasse erweitern, sie definieren das Schema der zugrunde liegenden Datenbank.
Unterstützte Feldtypen sind boolean
, byte
, short
, int
, long
, float
, double
, String
, Date
, byte[]
, Verknüpfungen zu anderen RealmObject
und 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
}
Wenn Sie Ihrem RealmObject ein neues Feld hinzufügen (oder entfernen) (oder Sie eine neue RealmObject-Klasse hinzufügen oder ein vorhandenes löschen), ist eine Migration erforderlich. Sie können deleteIfMigrationNeeded()
in Ihrem RealmConfiguration.Builder
festlegen oder die erforderliche Migration definieren. Eine Migration ist auch erforderlich, wenn Sie die @Required
@Index
oder @PrimaryKey
@Required
oder @Index
@PrimaryKey
Annotation hinzufügen (oder entfernen).
Beziehungen müssen manuell festgelegt werden. Sie basieren NICHT automatisch auf Primärschlüsseln.
Seit 0.88.0 ist es auch möglich, öffentliche Felder anstelle von privaten Feldern / Getters / Setters in RealmObject-Klassen zu verwenden.
Es ist auch möglich zu implementieren RealmModel
statt erstreckt RealmObject
, wenn die Klasse auch mit Anmerkungen versehen ist @RealmClass
.
@RealmClass
public class Person implements RealmModel {
// ...
}
In diesem Fall werden Methoden wie person.deleteFromRealm()
oder person.addChangeListener()
durch RealmObject.deleteFromRealm(person)
und RealmObject.addChangeListener(person)
.
Einschränkungen bestehen darin, dass durch ein RealmObject
nur RealmObject
erweitert werden kann und final
, volatile
und transient
Felder nicht unterstützt werden.
Es ist wichtig, dass eine verwaltete RealmObject-Klasse nur in einer Transaktion geändert werden kann. Ein verwaltetes RealmObject kann nicht zwischen Threads übergeben werden.
Liste der Grundelemente (RealmList )
Realm unterstützt derzeit nicht das Speichern einer Liste von Grundelementen. Es ist auf ihrer ToDo-Liste ( GitHub-Ausgabe Nr. 575 ), aber in der Zwischenzeit ist hier eine Problemumgehung.
Erstellen Sie eine neue Klasse für Ihren primitiven Typ, dies verwendet Integer, aber ändern Sie sie für das, was Sie speichern möchten.
public class RealmInteger extends RealmObject {
private int val;
public RealmInteger() {
}
public RealmInteger(int val) {
this.val = val;
}
// Getters and setters
}
Sie können dies jetzt in Ihrem RealmObject
.
public class MainObject extends RealmObject {
private String name;
private RealmList<RealmInteger> ints;
// Getters and setters
}
Wenn Sie GSON
, um Ihr RealmObject
, müssen Sie einen benutzerdefinierten RealmObject
hinzufügen.
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();
Try-With-Ressourcen
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
}
Die Try with-Ressourcen können nur von KITKAT verwendet werden (minSDK 19)
Sortierte Abfragen
Um eine Abfrage zu sortieren, sollten Sie anstelle von findAll()
findAllSorted()
.
RealmResults<SomeObject> results = realm.where(SomeObject.class)
.findAllSorted("sortField", Sort.ASCENDING);
Hinweis:
sort()
gibt ein völlig neues RealmResults zurück, das sortiert ist, aber ein Update dieses RealmResults setzt es zurück. Wenn Sie sort()
, sollten Sie es immer in Ihrem RealmChangeListener
neu sortieren, den RealmChangeListener
aus den vorherigen RealmResults
und den zurückgegebenen neuen RealmResults
. Die Verwendung von sort()
für ein RealmResults
von einer noch nicht geladenen RealmResults
Abfrage zurückgegeben wird, RealmResults
fehl.
findAllSorted()
gibt die Ergebnisse immer nach Feld sortiert zurück, auch wenn es aktualisiert wird. Es wird empfohlen, findAllSorted()
.
Async-Abfragen
Jede synchrone Abfragemethode (wie findAll()
oder findAllSorted()
) hat ein asynchrones Gegenstück ( findAllAsync()
/ findAllSortedAsync()
).
Asynchrone Abfragen lagern die Auswertung der RealmResults
in einen anderen Thread aus. Um diese Ergebnisse im aktuellen Thread zu erhalten, muss der aktuelle Thread ein Looper-Thread sein (read: async-Abfragen funktionieren normalerweise nur im UI-Thread).
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);
Verwenden von Realm mit RxJava
Für Abfragen stellt Realm die Methode realmResults.asObservable()
zur Verfügung. Das Beobachten von Ergebnissen ist nur für Looper-Threads (normalerweise der UI-Thread) möglich.
Damit dies funktioniert, muss Ihre Konfiguration Folgendes enthalten
realmConfiguration = new RealmConfiguration.Builder(context) //
.rxFactory(new RealmObservableFactory()) //
//...
.build();
Anschließend können Sie Ihre Ergebnisse als beobachtbare verwenden.
Observable<RealmResults<SomeObject>> observable = results.asObservable();
Bei asynchronen Abfragen sollten Sie die Ergebnisse nach isLoaded()
filtern, damit Sie nur dann ein Ereignis erhalten, wenn die Abfrage ausgeführt wurde. Dieses filter()
wird nicht für synchrone Abfragen benötigt ( isLoaded()
gibt bei Synchronisationsabfragen immer true
).
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));
Für Schreibvorgänge sollten Sie entweder die Methode executeTransactionAsync()
verwenden oder eine Realm-Instanz im Hintergrundthread öffnen, die Transaktion synchron ausführen und dann die Realm-Instanz schließen.
public Subscription loadObjectsFromNetwork() {
return objectApi.getObjects()
.subscribeOn(Schedulers.io())
.subscribe(response -> {
try(Realm realmInstance = Realm.getDefaultInstance()) {
realmInstance.executeTransaction(realm -> realm.insertOrUpdate(response.objects));
}
});
}
Grundlegende Verwendung
Instanz einrichten
Um Realm zu verwenden, müssen Sie zuerst eine Instanz davon erhalten. Jede Realm-Instanz ist einer Datei auf der Festplatte zugeordnet. Der einfachste Weg, um eine Instanz zu erhalten, lautet wie folgt:
// 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();
Die Methode Realm.getInstance()
erstellt die Datenbankdatei, falls sie noch nicht erstellt wurde. Andernfalls wird die Datei geöffnet. Das RealmConfiguration
Objekt steuert alle Aspekte der RealmConfiguration
eines Realms - ob in einer inMemory()
-Datenbank, Name der Realm-Datei, ob der Realm gelöscht werden soll, wenn eine Migration erforderlich ist, Anfangsdaten usw.
Bitte beachten Sie, dass Aufrufe von Realm.getInstance()
Referenzzähler sind (jeder Aufruf erhöht einen Zähler) und der Zähler wird beim realm.close()
dekrementiert.
Eine Instanz schließen
Auf Hintergrund - Threads, ist es sehr wichtig , die Realm - Instanz (en) zu schließen , sobald es nicht mehr verwendet wird (zum Beispiel Transaktion abgeschlossen ist und die Thread - Ausführung endet). Wenn Sie nicht alle Realm-Instanzen im Hintergrund-Thread schließen, führt dies zu einem Versionspinning und kann zu einer erheblichen Vergrößerung der Dateigröße führen.
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
Es ist erwähnenswert, dass Sie oberhalb von API Level 19 diesen Code durch Folgendes ersetzen können:
try(Realm realm = Realm.getDefaultInstance()) {
// ...
}
Modelle
Der nächste Schritt wäre das Erstellen Ihrer Modelle. Hier könnte eine Frage gestellt werden: "Was ist ein Modell?". Ein Modell ist eine Struktur, die Eigenschaften eines Objekts definiert, das in der Datenbank gespeichert wird. Im Folgenden modellieren wir beispielsweise ein Buch.
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;
}
}
Beachten Sie, dass Ihre Modelle die RealmObject-Klasse erweitern sollten. Der Primärschlüssel wird auch durch @PrimaryKey
Annotation @PrimaryKey
angegeben. Primärschlüssel können Null sein, aber nur ein Element kann null
als Primärschlüssel haben. Sie können auch die Annotation @Ignore
für die Felder verwenden, die nicht auf dem Datenträger gespeichert werden sollen:
@Ignore
private String isbn;
Daten einfügen oder aktualisieren
Um ein Buchobjekt in Ihrer Realm-Datenbankinstanz zu speichern, können Sie zuerst eine Instanz Ihres Modells erstellen und diese dann mit der copyToRealm
Methode in der Datenbank copyToRealm
. Zum Erstellen oder Aktualisieren können Sie copyToRealmOrUpdate
. (Eine schnellere Alternative ist das neu hinzugefügte 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);
}
});
Beachten Sie, dass alle Änderungen an Daten in einer Transaktion erfolgen müssen. Eine andere Methode zum Erstellen eines Objekts ist das folgende Muster:
Book book = realm.createObject(Book.class, primaryKey);
...
Datenbank abfragen
Alle Bücher:
RealmResults<Book> results = realm.where(Book.class).findAll();
Alle Bücher mit einer ID größer als 10:
RealmResults<Book> results = realm.where(Book.class) .greaterThan("id", 10) .findAll();
Bücher von
'Taylor Swift'
oder'%Peter%'
:RealmResults<Book> results = realm.where(Book.class) .beginGroup() .equalTo("author", "Taylor Swift") .or() .contains("author", "Peter") .endGroup().findAll();
Objekt löschen
Zum Beispiel möchten wir alle Bücher von Taylor Swift löschen:
// 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();
}
});