Realm

Realm’s mobile database is an open source, developer-friendly alternative to CoreData and SQLite.

It has been owned by MongoDB since spring 2019.

dependency

Realm is added via gradle plugin. So in top-level build.gradle you must specify jcenter repository and path buildscript block. And mavenCentral repository to all projects.

buildscript {
    repositories {
        // ...
        jcenter()
    }
    dependencies {
        // ...
        classpath "io.realm:realm-gradle-plugin:10.4.0"
    }
}

allprojects {
    repositories {
       // ...
        mavenCentral()
    }
    
    //...
}

In app-level build.gradle you must apply plugins

// in this order
apply plugin: 'kotlin-kapt' // for kotlin
apply plugin: 'realm-android'

initialization

Initialize Realm engine by Context object from Activity or Application subclass.

Realm.init(this)

Create default configuration

val config = RealmConfiguration.Builder()
    .name("mobibrowser-realm")
    .initialData {
        it.insert(Suggestion("wikipedia.com", "https://", true))
        it.insert(Suggestion("google.com", "https://", true))
        it.insert(Suggestion("youtube.com", "https://", true))
        it.insert(Suggestion("socode4.com", "https://", true))
     }
    .schemaVersion(1)
    .allowQueriesOnUiThread(true)
    .allowWritesOnUiThread(true)
            //.compactOnLaunch()
            //.inMemory()
    .build()
 
Realm.setDefaultConfiguration(config) 

Application may have several realms. The name() allows to specify which to use.

By default transactions are disallowed in UI thread. You can change it with allowQueriesOnUiThread() and allowWritesOnUiThread() methods.

Default configuration allows to get default instance of realm for current thread.

val realm = Realm.getDefaultInstance()
// do something ...
realm.close() 

entities

Entities of realm model must extend the RealmObject class. It must have empty constructor. In Kotlin an entity class must be open.

open class Suggestion(
    @Index var text: String,
    var scheme: String = "",
    var predefined: Boolean = false
) : RealmObject() {
    @PrimaryKey
    var _id = ObjectId()
    var used = System.currentTimeMillis()

    constructor() : this("", "", false)
}

You can use following annotations for fields

annotation description
@Index Adds a search index to the field. It will cause that the Realm file larger and inserts slower but queries will be faster.
@PrimaryKey Marks a field as a primary key inside Realm. Only one field in a RealmObject class can have this annotation, and the field should uniquely identify the object. If you not mark any field, default primary key will be used.
@Ignore Field will be ignored by Realm.

Realm support all base types like numbers and string. For a list fields use RealmList class.

CRUD

The query results returned by Realm are thread-local views to the current "database version", and these views "automatically update" when a transaction is committed from any thread.

val realm = Realm.getDefaultInstance()
val results1 = realm.where(Suggestion::class.java).findAllAsync()

val results2 = realm.where(Suggestion::class.java)
            .beginGroup()
            .equalTo(fieldName, fieldValue)
            .or()
            .contains(fieldName, value) // SQL equalent to %value%
            .endGroup().findAll()

All write operations must be within transaction.

fun saveSuggestion(obj: Suggestion) {
    realm.executeTransactionAsync {
        it.insertOrUpdate(obj)
    }
}

fun deleteOldSuggestions() {

    realm.executeTransactionAsync {

        val time = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(10)

        it.where(Suggestion::class.java)
          .lessThan("timeAccess", time)
          .equalTo("temporary", true)
          .findAll()
          .deleteAllFromRealm()
        }
}