Why Realm is a great persistence solution for beginners in Android development

  • Realm is a database, which isn’t SQLite-based (it brings its own core)
  • it’s generally called an “object database”: your objects are mapped directly, and many-relations are mapped directly as a list, instead of with JOINs across multiple tables
  • Realm not only handles storing your data, but it also keeps queried data up to date, and calls any registered change listeners when your data has been modified, allowing you to keep the UI up to date with minimal effort
  • Most importantly, due to lazy evaluation of RealmResults’ elements, you don’t need to implement pagination logic — just get a RealmResults, throw it in a RealmRecyclerViewAdapter, and it’s good to go — in that case, even the listener that keeps the RecyclerView updated is managed automatically.

Setting things up

buildscript {
repositories {
jcenter()
}
dependencies {
classpath "io.realm:realm-gradle-plugin:4.3.1"
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'realm-android'
dependencies {
kapt 'dk.ilios:realmfieldnameshelper:1.1.1'
implementation
'io.realm:android-adapters:2.1.1'
}
class CustomApplication: Application() {
override fun onCreate() {
super.onCreate()
Realm.init(this)
Realm.setDefaultConfiguration(
RealmConfiguration.Builder()
.deleteIfMigrationNeeded()
.build())
}
}
<application android:name=".CustomApplication"

What are some other cool things you can do with Realm?

val person: Person? = realm.where<Person>()
.equalTo(PersonFields.NAME, "John")
.findFirst()
person?.dogs?.let { dogs ->
val filteredDogs = dogs.where()
.greaterThan(DogFields.AGE, 4)
.findAll()
}
implementation 'io.realm:android-adapters:2.1.1'class PersonAdapter(val results: RealmResults<Person>): 
RealmRecyclerViewAdapter<Person, PersonViewHolder>(results, true) {
...
}

What to look out for?

  • RealmObjects cannot extend any classes that are not specifically RealmObject — which means you can’t inherit fields, and your RealmObject classes cannot inherit from other RealmObjects.
  • You can only store the field types that are allowed (…, boolean, String, Date, Long, Double, ByteArray, specific RealmObject types, RealmList)
    Using @Ignore lets you add unsupported fields to the class, but that is not stored, of course.
  • Your RealmObject classes define the underlying schema, so if you modify the fields (or their attributes), unless you drop/recreate with deleteIfMigrationNeeded(), you’ll also need to track these changes with schema version bump, and with migration
    (which is like ALTER TABLE statements in onUpgrade() using SQLite, except it’s done with the DynamicRealm API, for example realmSchema.get("Dog").addIndex("age"))
  • Cascade behavior is done by-hand (manual)
  • Realm instances (and anything obtained from it — queries, objects, query results) are thread-local, so when you do the background write, you need a Realm instance for that (which you close via use {.) When you set up the auto-refreshing query, you need a Realm instance for that, which you obtained on the UI thread.
    So you can’t just use a singleton Realm instance for the whole app, and you can’t toss RealmResults and managed objects between threads.
  • You should never ever leave a Realm instance open once you’re done with it on a background thread. Realm.getInstance() sounds like it gives you a single instance, but it also increases a reference count. So you should always make sure Realm.getInstance() is paired with close() somewhere.
    You can also use .use { (or try(Realm realm = … in Java), like in the above example. This is written down quite well in the docs, though.

Why would I use Realm instead of SQLite/DbFlow/Requery/SQLDelight/Room?

@Entity
data class Person(@PrimaryKey val id: Int) {
}
@Entity(foreignKeys = @ForeignKey(entity = Person.class,
parentColumns = "id",
childColumns = "personId",
onDelete = ForeignKey.CASCADE))
data class Dog(@PrimaryKey val id: Int) {
}
@Entity(tableName = "user_dog_join",
primaryKeys = ["personId", "dogId"],
foreignKeys = [
@ForeignKey(entity = Person.class,
parentColumns = "id",
childColumns = "personId"),
@ForeignKey(entity = Dog.class,
parentColumns = "id",
childColumns = "dogId")
])
data class PersonDogJoinTable(val userId: Int, val dogId: Int) {
}
class PersonWithDogs {
@Embedded var person: Person

@Relation(parentColumn = "id",
entityColumn = "personId") var
dogList: List<Dog>
}
open class Person : RealmObject() {
var dogs: RealmList<Dog>? = null
}
open class Dog: RealmObject() {
@LinkingObjects("dogs")
val owners: RealmResults<Person>? = null
}

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store