Android Realm Database

Filed Under: Android

In this tutorial, we’ll be discussing the basics of Realm Database and implement it in our Android Application for offline data storage.

Android Realm Database

Realm is a database that is the perfect alternative to SQLite. It’s is a NoSQL database designed for mobile platform.
Its core consists of a self-contained C++ library. It supports Android and iOS both.

Pros of Realm

  • Simplicity – Unlike SQLite the code is much shorter and concise. Besides, instead of using SQL, you just need to deal with objects and object trees in Realm.
  • Speed – Despite having a complex algorithm underneath, Realm performs CRUD operations faster. Obtaining objects is very fast since there is no deserialization
  • Live Objects – Realm has a no copy behaviour. All fetches are lazy, and the data is never copied until you perform some operations on them.
    So till then from a query, all that you get is pointers to data. Hence all objects received from Realm are proxy to the database. Due to this, zero copy is achieved. Whenever you need to access the data you will always get the latest value.
  • Great Documentation – Realm documentation is clear and the community support is great.
  • JSON Support Built-in – Realm has JSON support. You can directly set data from JSON without the need to create model classes.
  • Security – You can secure the database using encryption.
  • Reactive Programming – You can observe changes in the database and update the UI accordingly. Using RxJava along with Realm just makes it a lot more simple and fun.
  • Built-in Adapters – Realm has Adapter classes for the Android UI components.


  • Cons of Realm

  • No Auto-increment – You cannot auto-increment values in Realm.
  • Restrictions on Model classes – Besides the getter setter methods you cannot override methods like hashcode and equals in the Realm Model classes.
  • Threading – Real model classes can’t be passed from one thread to another. Hence you’ll have to query the class again on the different thread.
  • Configuring Realm in Android

    Add the following classpath in your root build.gradle file:

    
    buildscript {
        ...
        dependencies {
            ...
            classpath 'io.realm:realm-gradle-plugin:3.2.1'
        }
    }
    

    Add the following plugin to the app’s build.gradle

    
    apply plugin: 'realm-android'
    

    Creating a Realm Instance

    To create a Realm instance we do the following:

    
    Realm mRealm = Realm.getInstance(context);
    

    We can also pass a RealmConfiguration instance inside the getInstance()

    
    RealmConfiguration config =
                    new RealmConfiguration.Builder()
                            .name("test.db")
                            .schemaVersion(1)
                            .deleteRealmIfMigrationNeeded()
                            .build();
    


    Creating Realm Model class

    To create Model classes, extend them with RealmObject.
    Primary keys can be of String or integer type and are annotated with @PrimaryKey annotation.

    Following are some annotations commonly used on Realm Model classes:

    • @PrimaryKey
    • @Required – Cannot be null
    • @Index – Add this on fields you are using in Query. It makes the queries 4X faster.
    • @Ignore

    Writing Data to Realm

    You can write data to Realm in the following ways:

    
    Realm realm = Realm.getDefaultInstance();
    realm.beginTransaction();
    realm.copyToRealm(realmModelInstance)
    realm.commitTransaction();
    

    There is a problem with the above approach though. It doesn’t cancel the transaction if failed.
    Hence it is not the recommended way to insert data into the Database.

    The method executeTranscation or executeTranscationAsync automatically handle the cancel transaction.

    
    Realm realm = Realm.getDefaultInstance();
        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                realm.insertOrUpdate(realmModelInstance);
            }
        });
    

    Also, we can add a try-catch inside the executeTranscation to catch exceptions. Try-catch is ignored by the earlier approach though.

    insertOrUpdate or copyToRealmOrUpdate are used to insert if the primary key doesn’t exist or update the current object if the primary key exists.

    Read From Realm

    
    RealmResults<RealModelClass> results = realm.where(RealModelClass.class).findAll();
    

    Again it’s a good practice to use this inside the execute method.

    Delete From Realm

    
    mRealm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
                    RealmResults<RealModelClass> results = realm.where(RealModelClass.class).findAll();
                    results.deleteAllFromRealm();
                }
            });
    

    Following are the other methods that can be used to delete the results:

    android realm delete functions

    RealmList is a built-in collection of RealmObjects and is used for model one-to-many relationships.

    RealmList cannot support non-realm objects(such as Long, String, Integer).

    In the following section, we’ll be creating a basic Android Application which uses Realm and performs CRUD operations.

    Project Structure

    android realm project structure

    Code

    The code for the activity_main.xml layout is given below:

    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="8dp"
        android:orientation="horizontal"
        android:weightSum="2"
        tools:context=".MainActivity">
    
    
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical">
    
            <EditText
                android:id="@+id/inName"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Name" />
    
            <EditText
                android:id="@+id/inAge"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Age"
                android:inputType="number" />
    
            <EditText
                android:id="@+id/inSkill"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Skill" />
    
    
            <Button
                android:id="@+id/btnAdd"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="ADD" />
    
    
            <TextView
                android:id="@+id/textViewEmployees"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Result:" />
    
    
            <Button
                android:id="@+id/btnRead"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="READ" />
    
            <Button
                android:id="@+id/btnUpdate"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="UPDATE" />
    
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical">
    
            <TextView
                android:id="@+id/txtFilterByAge"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Result:" />
    
            <Button
                android:id="@+id/btnFilterByAge"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="EMPLOYEE OVER 25" />
    
    
            <TextView
                android:id="@+id/txtFilterBySkill"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Result:" />
    
    
            <Button
                android:id="@+id/btnDelete"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="DELETE BY NAME" />
    
            <Button
                android:id="@+id/btnDeleteWithSkill"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="DELETE ALL WITH SKILL" />
    
        </LinearLayout>
    
    </LinearLayout>
    

    Inside the MyApplication.java is where we initialise our Realm.

    
    package com.journaldev.androidrealmdatabase;
    
    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(getApplicationContext());
    
    
            RealmConfiguration config =
                    new RealmConfiguration.Builder()
                            .deleteRealmIfMigrationNeeded()
                            .build();
    
            Realm.setDefaultConfiguration(config);
        }
    }
    
    

    The code for the Employee.java class is given below:

    
    package com.journaldev.androidrealmdatabase;
    
    import io.realm.RealmList;
    import io.realm.RealmObject;
    import io.realm.annotations.PrimaryKey;
    import io.realm.annotations.Required;
    
    public class Employee extends RealmObject {
    
    
        public static final String PROPERTY_NAME = "name";
        public static final String PROPERTY_AGE = "age";
    
        @PrimaryKey
        @Required
        public String name;
        public int age;
    
        public RealmList<Skill> skills;
    }
    
    

    The code for the Skill.java class is given below:

    
    package com.journaldev.androidrealmdatabase;
    
    import io.realm.RealmObject;
    import io.realm.annotations.PrimaryKey;
    import io.realm.annotations.Required;
    
    
    public class Skill extends RealmObject {
    
        public static final String PROPERTY_SKILL = "skillName";
    
        @PrimaryKey
        @Required
        public String skillName;
    }
    
    

    The code for the MainActivity.java is given below:

    
    package com.journaldev.androidrealmdatabase;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import io.realm.Realm;
    import io.realm.RealmList;
    import io.realm.RealmResults;
    import io.realm.exceptions.RealmPrimaryKeyConstraintException;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
    
        Button btnAdd, btnRead, btnUpdate, btnDelete, btnDeleteWithSkill, btnFilterByAge;
        EditText inName, inAge, inSkill;
        TextView textView, txtFilterBySkill, txtFilterByAge;
        Realm mRealm;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            initViews();
    
            mRealm = Realm.getDefaultInstance();
        }
    
        private void initViews() {
            btnAdd = findViewById(R.id.btnAdd);
            btnAdd.setOnClickListener(this);
            btnRead = findViewById(R.id.btnRead);
            btnRead.setOnClickListener(this);
            btnUpdate = findViewById(R.id.btnUpdate);
            btnUpdate.setOnClickListener(this);
            btnDelete = findViewById(R.id.btnDelete);
            btnDelete.setOnClickListener(this);
            btnDeleteWithSkill = findViewById(R.id.btnDeleteWithSkill);
            btnDeleteWithSkill.setOnClickListener(this);
            btnFilterByAge = findViewById(R.id.btnFilterByAge);
            btnFilterByAge.setOnClickListener(this);
            textView = findViewById(R.id.textViewEmployees);
            txtFilterBySkill = findViewById(R.id.txtFilterBySkill);
            txtFilterByAge = findViewById(R.id.txtFilterByAge);
    
            inName = findViewById(R.id.inName);
            inAge = findViewById(R.id.inAge);
            inSkill = findViewById(R.id.inSkill);
        }
    
        @Override
        public void onClick(View view) {
    
            switch (view.getId()) {
                case R.id.btnAdd:
                    addEmployee();
                    break;
                case R.id.btnRead:
                    readEmployeeRecords();
                    break;
                case R.id.btnUpdate:
                    updateEmployeeRecords();
                    break;
                case R.id.btnDelete:
                    deleteEmployeeRecord();
                    break;
                case R.id.btnDeleteWithSkill:
                    deleteEmployeeWithSkill();
                    break;
                case R.id.btnFilterByAge:
                    filterByAge();
                    break;
            }
        }
    
        private void addEmployee() {
    
            Realm realm = null;
            try {
                realm = Realm.getDefaultInstance();
                realm.executeTransaction(new Realm.Transaction() {
                    @Override
                    public void execute(Realm realm) {
    
    
                        try {
    
    
                            if (!inName.getText().toString().trim().isEmpty()) {
                                Employee employee = new Employee();
                                employee.name = inName.getText().toString().trim();
    
                                if (!inAge.getText().toString().trim().isEmpty())
                                    employee.age = Integer.parseInt(inAge.getText().toString().trim());
    
    
                                String languageKnown = inSkill.getText().toString().trim();
    
                                if (!languageKnown.isEmpty()) {
                                    Skill skill = realm.where(Skill.class).equalTo(Skill.PROPERTY_SKILL, languageKnown).findFirst();
    
                                    if (skill == null) {
                                        skill = realm.createObject(Skill.class, languageKnown);
                                        realm.copyToRealm(skill);
                                    }
    
                                    employee.skills = new RealmList<>();
                                    employee.skills.add(skill);
                                }
    
                                realm.copyToRealm(employee);
                            }
    
                        } catch (RealmPrimaryKeyConstraintException e) {
                            Toast.makeText(getApplicationContext(), "Primary Key exists, Press Update instead", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            } finally {
                if (realm != null) {
                    realm.close();
                }
            }
        }
    
    
        private void readEmployeeRecords() {
    
    
            mRealm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
    
                    RealmResults<Employee> results = realm.where(Employee.class).findAll();
                    textView.setText("");
                    for (Employee employee : results) {
                        textView.append(employee.name + " age: " + employee.age + " skill: " + employee.skills.size());
                    }
                }
            });
    
    
        }
    
        private void updateEmployeeRecords() {
    
            mRealm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
    
    
                    if (!inName.getText().toString().trim().isEmpty()) {
    
    
                        Employee employee = realm.where(Employee.class).equalTo(Employee.PROPERTY_NAME, inName.getText().toString()).findFirst();
                        if (employee == null) {
                            employee = realm.createObject(Employee.class, inName.getText().toString().trim());
                        }
                        if (!inAge.getText().toString().trim().isEmpty())
                            employee.age = Integer.parseInt(inAge.getText().toString().trim());
    
                        String languageKnown = inSkill.getText().toString().trim();
                        Skill skill = realm.where(Skill.class).equalTo(Skill.PROPERTY_SKILL, languageKnown).findFirst();
    
                        if (skill == null) {
                            skill = realm.createObject(Skill.class, languageKnown);
                            realm.copyToRealm(skill);
                        }
    
    
                        if (!employee.skills.contains(skill))
                            employee.skills.add(skill);
    
                    }
                }
            });
        }
    
        private void deleteEmployeeRecord() {
            mRealm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
                    Employee employee = realm.where(Employee.class).equalTo(Employee.PROPERTY_NAME, inName.getText().toString()).findFirst();
                    if (employee != null) {
                        employee.deleteFromRealm();
                    }
                }
            });
        }
    
        private void deleteEmployeeWithSkill() {
            mRealm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
    
                    RealmResults<Employee> employees = realm.where(Employee.class).equalTo("skills.skillName", inSkill.getText().toString().trim()).findAll();
                    employees.deleteAllFromRealm();
                }
            });
        }
    
    
        private void filterByAge() {
            mRealm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
    
                    RealmResults<Employee> results = realm.where(Employee.class).greaterThanOrEqualTo(Employee.PROPERTY_AGE, 25).findAllSortedAsync(Employee.PROPERTY_NAME);
    
                    txtFilterByAge.setText("");
                    for (Employee employee : results) {
                        txtFilterByAge.append(employee.name + " age: " + employee.age + " skill: " + employee.skills.size());
                    }
                }
            });
        }
    
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mRealm != null) {
                mRealm.close();
            }
        }
    }
    
    

    Don’t forget to close the Realm instance!

    When you click update with a different Skill that doesn’t exist, it creates that and adds it to the RealmList.

    The output of the above application in action is given below:

    android realm output

    That brings an end to this tutorial. You can download the project from the link below:

    Comments

    1. Kartik Dubey says:

      can u make full tutorial or example of flexible app update??

    2. Arsalan Tahir says:

      This is a very good project. But you could have made this post better by explaining and going through each step instead of just throwing out the code.

    3. Pooja Patel says:

      Hi,
      I try to add override methods “equals” and “hashcode” in my Model Class which extends a “RealmObject” and i am not getting any error for this methods.

    4. Nader Nagi says:

      on “addEmployee” method, you use “createObject/copyToRealm” method for Skill.
      what’s the difference between them?

    5. mi says:

      why realm .. there is a room !!!

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    close
    Generic selectors
    Exact matches only
    Search in title
    Search in content
    Search in posts
    Search in pages