Realm is a lightweight database that can replace both SQLite and ORM libraries in your Android projects.
Compared to SQLite, Realm is faster and has lots of modern features, such as JSON support, a fluent API, data change notifications, and encryption support, relationships, migrations all of which make life easier for Android developers. Realm occupies very less memory space compared to SQLite.
Realm has own C++ core and aims to provide a mobile-first alternative to SQLite. Realm store data in a universal, table-based format by a C++ core.
A detailed explanation on Realm internals, by Christian Melchior is here.
Installing
Installing Realm as a Gradle plugin is a two step process.
Step 1: Add the following class path dependency to the project level build.gradle file.
buildscript { repositories { jcenter() } dependencies { classpath "io.realm:realm-gradle-plugin:2.3.0" } }
Step 2: Apply the realm-android plugin to the top of application level build.gradle file.
apply plugin: 'realm-android'
A Realm is similar to a SQLite database. It has a file associated with it, which, once created, will persist on Android’s file system. You can find .realm
file under the Data/Data/Package Name/files directory and pull it to your computer.
To create a new Realm, you can call the static Realm.getInstance
method from inside any Activity
.
Realm db = Realm.getInstance(context);
Note that calling Realm.getInstance
, without passing a RealmConfiguration
to it, results in the creation of a Realm file called default.realm.
If you want to add another Realm to your app, you must use a RealmConfiguration.Builder
object and give the Realm file a unique name.
Realm db = Realm.getInstance( new RealmConfiguration.Builder(context) .name("movies.realm") .build() );
CRUD operations
Any Java class that is serializable and has a default constructor and has getter/setter methods for its member variablescan can be stored in a Realm once it extends the RealmObject
class. For example, instances of the following class can be easily stored in a Realm:
public class Movie extends RealmObject { @PrimaryKey private String id; private String title; private int year; public Movie() { } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } }
Primary keys can be of String or integer type and should be annotated by @PrimaryKey
annotation. An index on a field is created with an @Index
annotation with the primary key indexed implicitly.
Realm supports following annotations
@Required
enforces checks to disallow null values.@Ignore
marks field should not be persisted to disk.@Index
adds a search index to the field.@PrimaryKey
uniquely identifies each record in the databaseRealm supports boolean, byte, short, int, long, float, double, string, date, byte[] data types. The boxed types Boolean, Byte, Short, Integer, Long, Float, Double can also be used in model class. Using these types, it is possible to set the value of a field to null.
Realm forces you to execute all write operations inside a transaction. To start a new transaction, use the beginTransaction
method. Similarly, to end the transaction, use the commitTransaction
method.
You always have an opportunity to cancel your transactions, just call:
realm.cancelTransaction();
Here’s how you would create and save an instance of the Movie
class:
db.beginTransaction(); // create an object Movie movie = db.createObject(Movie.class); // set its fields movie.setTitle("Movie 1"); movie.setYear(2016); db.commitTransaction();
You might have noticed that movie
was not created using the constructor of the Movie
class. For a Realm to manage an instance of a RealmObject
, the instance must be created using the createObject
method.
If you must use the constructor, don’t forget to use the copyToRealm
method of the relevant Realm object before you commit the transaction. Here’s an example:
// create the object Movie movie = new Movie(); movie.setTitle("Movie 2"); movie.setYear(2017); db.beginTransaction(); Movie copyMovie = db.copyToRealm(movie); db.commitTransaction();
Realm does not currently support auto increment of Primary Key. There are three work-around regarding auto increment of primary key.
First way. Use String
as ID and initialize it as
String id = UUID.randomUUID().toString();
Second way. Query for the max primary key value each time you want to insert data and then increment that by 1 and set as the primary key.
long id = db.where(Movie.class).max("id").longValue(); movie.setId(id);
Third way. Run the query to find the ID once, probably when the app launches and then keep a reference to that max ID value and then increment and get that value as needed like so:
AtomicLong movieKey = new AtomicLong(db.where(Movie.class).max("id").longValue() + 1); long id = movieKey.getAndIncrement(); movie.setId(id);
Realm offers a very intuitive and fluent API for creating queries. To create a query, use the where()
method of the relevant Realm object and pass the class of the objects you are interested in. After creating the query, you can fetch all results using the findAll()
method, which returns a RealmResults
object. In the following example, we fetch and print all objects of type Movie
:
RealmResults<Movie> movies = db.where(Movie.class).findAll(); for(Movie movie : movies) { Log.d(TAG, movie.getTitle()); }
Fetch one object
Movie movie = db.where(Movie.class).equalTo("id", id).findFirst();
Realm offers several aptly named methods, such as beginsWith()
, endsWith()
, lesserThan()
and greaterThan()
, you can use to filter the results. The following code shows you how you can use the greaterThan()
method to fetch only those Movie
objects whose year is greater than 2016:
RealmResultsmovies = db.where(Movie.class).greaterThan("year", 2016).findAll();
or query field which contains something
db.where(Movie.class) .contains("title", "Movie 1") .or() .contains("title", "Movie 2") .findAll();
If you want the results of the query to be sorted, you can use the findAllSorted()
method. As its arguments, it takes a String
specifying the name of the field to sort by and a Boolean
specifying the sort order.
// sort by title, in descending order RealmResults<Movie> movies = db.where(Movie.class).findAllSorted("title", false);
Update object
Movie movie = db.where(Movie.class).equalTo("id", 1).findFirst(); db.beginTransaction(); movie.setTitle("Movie 3"); db.commitTransaction();
Delete object
RealmResults<Movie> movies = db.where(Movie.class).equalTo("id", 1).findAll(); db.beginTransaction(); movies.remove(0); db.commitTransaction();
Clear all
db.beginTransaction(); db.clear(Movie.class); db.commitTransaction();
Importing objects
We can import list of objects to Realm from List
that was created before.
for (Movie m : movies) { db.beginTransaction(); db.copyToRealm(m); db.commitTransaction(); }
It’s also possible to load data into the Realm database from JSON format represented by a String
, InputStream
or JSONObject
. A single object can be added by using the createObjectFromJson()
db.beginTransaction(); db.createObjectFromJson(Movie.class, "{ title: \"Movie 4\", year: 1991 }"); db.commitTransaction();
A list of objects is loaded using the createAllFromJson()
method
// InputStream is = new FileInputStream(new File("path_to_file")); Resources resources = getResources(); InputStream is = resources.openRawResource(R.raw.movies); db.beginTransaction(); db.createAllFromJson(Movie.class, is); db.commitTransaction();
Relationships
Also Realm supports Many-to-One, Many-to-Many relationships.
Realm supports creating relationships using data models in a very intuitive way. To link another object in a database, simply declare a field of the appropriate type (one-to-one). A single object can be linked from several other objects too (one-to-many).
Establishing relationship to any number of objects is possible via a RealmList<T>
field declaration. RealmList
behaves very much like regular Java List
object. A nice feature is that Realm will never return an instance of RealmList
with null
value. The returned object is always a list, possibly empty.
Example
Let's modify Application
instance and add Realm configurations. Create a class named MyApplication.java
under app
package.
import android.app.Application; import io.realm.Realm; import io.realm.RealmConfiguration; public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Realm.init(this); RealmConfiguration cfg = new RealmConfiguration.Builder() .name(Realm.DEFAULT_REALM_NAME) .schemaVersion(0) .deleteRealmIfMigrationNeeded() .build(); Realm.setDefaultConfiguration(cfg); } }
Setting a default configuration in your custom Application
class, will ensure that it is available in the rest of your code.
Open AndroidManifest.xml and add the MyApplication
to <application>
tag.
<application android:name=".MyApplication" ...> </application>
In the model
package add a class named Movie.java
, see above. Realm data models are created by extending the the RealmObject
base class. A Realm data model also supports public, protected and private fields as well as custom methods.
We'll create MainActivity
with simple layout and two buttons. First, button will add new Movie
record , second button will list all records to console.
public class MainActivity extends AppCompatActivity { private Realm db; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); db = Realm.getDefaultInstance(); } public void addMovie(View v) { String id = UUID.randomUUID().toString();; Movie movie = new Movie(); movie.setId(id); movie.setTitle("Movie " + id); movie.setYear(2016); db.beginTransaction(); db.copyToRealm(movie); db.commitTransaction(); } public void listMovie(View v) { RealmResults<Movie> movies = db.where(Movie.class).findAll(); for(Movie movie : movies) { Log.d(TAG, movie.getTitle()); } } @Override protected void onDestroy() { super.onDestroy(); // it is good practice to close Realm object db.close(); } }
Debugging
Under Linux we can use Stetho debug tool together with a Realm plugin.
Useful links