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();
    }
});


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow