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


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow