Exploring the vastness of the Realm, a cross-platform mobile database

August 10, 2016
Written by

H_dJY48ao0Rdvt9WElZz9ej36jhUWnnkHzr51pdiDe_c-a-Htrr4ov5QgQ72KLTuhy01F92BME1BBx2Wa59MlXg0u7ty6_FSlPzVSRPLOHy82LENnkc00t4uCXTb7WUqXmJmBzt4

If you’re an Android developer chances are you’ve used SQLite for internal storage in your mobile apps and know how painful it is to set it up.

Realm is a cross-platform mobile database engine that aims to provide developers with a mobile-first engine, super powered with fluent interfaces and field annotations. Here’s an example of how easy it is to use:

public class Note extends RealmObject {
    public String mText
}
Note note = new Note(); note.mText = "My awesome note";

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

Let’s look at how to build an application that stores your notes in a Realm database.

What You’ll Need

There are no other requirements to complete this tutorial since all you need is Android Studio, though you could probably use any other IDE’s you prefer.

If you don’t feel like going through the entire process of setting up the app, feel free to just clone my Github repository here.

Setting up

Create a new project in Android Studio called RealmNotes and choose a location for it. I usually store my Android apps in ~/Projects/Android/, but feel free to put it anywhere you like.

Choose API 16 as the Minimum SDK and Basic Activity as your starting point for this application. This will come with a pre-baked Floating Action Button which we will modify and use later on.

Click Finish on the next screen and you’ll be ready to start building the application.

NewProject.gif

Getting our dependencies

Open up your project-level build.gradle and add the JitPack repository to it so that we can install libraries straight from GitHub. Then add a dependency for the Realm plugin:

buildscript {
   repositories {
       jcenter()
   }
   dependencies {
       classpath 'com.android.tools.build:gradle:2.1.2'
       classpath 'io.realm:realm-gradle-plugin:1.1.1'
   }
}

allprojects {
   repositories {
       jcenter()
       maven { url "https://jitpack.io" }
   }
}

Now open your application-level build.gradle and apply the Realm plugin and install some libraries:

apply plugin: 'com.android.application'
apply plugin: 'realm-android'

// other stuff commented out for brevity

dependencies {
   compile fileTree(dir: 'libs', include: ['*.jar'])
   testCompile 'junit:junit:4.12'
   compile 'com.android.support:appcompat-v7:24.1.1'
   compile 'com.android.support:design:24.1.1'

   compile 'com.android.support:recyclerview-v7:24.1.1'
   compile 'com.github.thorbenprimke:realm-recyclerview:0.9.23'
}

We’ve added a library called RealmRecyclerView which is a RecyclerView wrapper for working with Realm as a datastore. It gives us some nice functionalities like pull-to-refresh and swipe-to-delete out of the box.

One thing to note here is that the major version of the RecyclerView needs to match the version on compileSdkVersion.

The IDE will ask you to sync. Let it do its thing as we’re just about to work on the app’s interface.

User Interface

Open up app/res/layout/activity_main.xml and change the floating action button icon to have a “+” instead of the default image:

<android.support.design.widget.FloatingActionButton
   android:id="@ id/fab"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="bottom|end"
   android:layout_margin="@dimen/fab_margin"
   android:src="@android:drawable/ic_input_add" />

We’re now going to add a TextField and a RecyclerView to our layout so we can add and display our notes. Open app/res/layout/content_main.xml, delete the existing code for the TextView and replace with:

<EditText
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:id="@+id/et_text"
   android:layout_alignParentTop="true"
   android:layout_alignParentLeft="true"
   android:layout_alignParentStart="true"
   android:layout_alignParentRight="true"
   android:layout_alignParentEnd="true" />

<co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView
   android:id="@+id/rv_notes"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   app:rrvIsRefreshable="false"
   app:rrvLayoutType="LinearLayout"
   app:rrvSwipeToDelete="true"
   android:layout_below="@ id/et_text"
   android:layout_alignParentLeft="true"
   android:layout_alignParentStart="true" />

Each one of the items in a RecyclerView is displayed individually and the rows inflate their own layout. Let’s create this layout by creating a new Resource File in app/res/layout called note_item.xml and replace its contents with:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:id="@+id/holder_container"
    android:layout_centerVertical="true"
    android:paddingBottom="10dp"
    android:paddingTop="10dp"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:orientation="vertical"
        >

        <TextView
            android:id="@+id/tv_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:textSize="15sp"
            style="@style/Base.TextAppearance.AppCompat.Headline"/>

        <TextView
            android:id="@+id/tv_date"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="10sp"
            style="@style/Base.TextAppearance.AppCompat.Subhead"/>

    </LinearLayout>
</RelativeLayout>

I think we should give it a spin now by pressing the Run button to make sure everything is working well. If you see something like this you’re all good.

first_spin.png

Notice that if you try to create a note right now nothing will happen as we’ve still not hooked this application up with our database. We will move on to that right now.

Entering the Realm

We need to get our view to send information to our Activity, which will have all the knowledge of our database. But first we need to tell our application what we want to store.

Go to app/java/{your_chosen_domain}.realmnotes and create a new Java class called Note.java. This class will contain the definition of our database along with any defaults we may wish to create such as IDs and dates:

import java.util.Calendar;
import java.util.Date;
import java.util.UUID;

import io.realm.RealmModel;
import io.realm.annotations.PrimaryKey;
import io.realm.annotations.RealmClass;

@RealmClass
public class Note implements RealmModel {
  @PrimaryKey
  private String id = UUID.randomUUID().toString();;
  private String text;
  private Date date = new Date(java.text.DateFormat.getDateTimeInstance().format(Calendar.getInstance().getTime()));

  public Note(){ }

  public Note(String text) { this.text = text; }

  public void setText(String text) { this.text = text; }
  public String getText() { return text;}
  public void setId(String id) { this.id = id; }
  public String getId() { return id; }
  public Date getDate() { return date; }
  public void setDate(Date date) { this.date = date; }
}

As you can see this is just a POJO that implements RealmModel.

This is important because it tells Realm this is not only an Object, but a RealmObject. You can find more about Realm entity relationships here. It also has a class annotation that lets Realm know this is a database object.

Now that we’ve defined what our database is going to look like, let’s head back to app/java/{your_chosen_domain}.realmnotes and open MainActivity.

Start by adding member variables to all the widgets we’re going to use to the top of the class and then on the onCreate method add the references to those:

public class MainActivity extends AppCompatActivity {
  private Realm mRealm;
  private RealmConfiguration mRealmConfig;
  private EditText mText;
  private RealmRecyclerView mNotes;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   mRealmConfig = new RealmConfiguration
                .Builder(this)
                .build();
   Realm.setDefaultConfiguration(mRealmConfig);
   mRealm = Realm.getDefaultInstance();
   mText = (EditText) findViewById(R.id.et_text);
   mNotes = (RealmRecyclerView) findViewById(R.id.rv_notes);

   Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
   setSupportActionBar(toolbar);

   FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
   fab.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                   .setAction("Action", null).show();
       }
   });
  }
}

We’ve also added the mRealmConfig and mRealm definitions, which initialise Realm. Running the application now and clicking the plus button will result in a SnackBar message being displayed on the screen and nothing more.

snackbar_400.png

The SnackBar says “Replace with your own action” which is convenient as this is exactly what we’re going to do now. Change the floating action button’s onClickListener to call the insertNote method :

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    if(mText.getText().length() > 0){
      insertNote(mText.getText().toString());
      mText.setText("");
      Snackbar.make(view, "Note added!", Snackbar.LENGTH_LONG)
     .setAction("Action", null).show();
    }
  }
});

Now create that method below the onCreate method and define it so it takes a String and uses Realm to add to the database:

private void insertNote(final String noteText) {
   mRealm.executeTransaction(new Realm.Transaction() {
       @Override
       public void execute(Realm realm) {
           Note note = new Note();
           note.setText(noteText);
           mRealm.copyToRealm(note);
       }
   });
}

The above is all it takes to add a new item to the database. We initialise our object and the copy it straight to Realm. Give it another spin and you should see that when you add a new note, the SnackBar shows a different message.

You will also notice that nothing gets displayed as we still aren’t loading the messages from our database.

Exploring the Realm

tumblr_mfpcef5sXl1rl04amo1_500.gif

We should now have a database that is eager to display some results so let’s satisfy that by fetching those results from the database and displaying them on our RecyclerView.

Start off by creating an Adapter that will serve as a bridge between our dataset and our widget. Create a new inner class in MainActivity called NoteRecyclerViewAdapter that extends RealmBasedRecyclerViewAdapter.

If we were using a normal RecyclerView we would just extend RecyclerViewAdapter. Since we’re using a modified version of it we need to extend a different class:

public class NoteRecyclerViewAdapter extends RealmBasedRecyclerViewAdapter<
           Note, NoteRecyclerViewAdapter.ViewHolder> {
  
   public NoteRecyclerViewAdapter(
           Context context,
           RealmResults<Note> realmResults) {
       super(context, realmResults, true, false);
   }

   public class ViewHolder extends RealmViewHolder {
       private TextView mText;
       private TextView mDate;

       public ViewHolder(RelativeLayout container) {
           super(container);
           this.mText = (TextView) container.findViewById(R.id.tv_text);
           this.mDate = (TextView) container.findViewById(R.id.tv_date);
       }
   }

   @Override
   public ViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int viewType) {
       View v = inflater.inflate(R.layout.note_item, viewGroup, false);
       return new ViewHolder((RelativeLayout) v);
   }

   @Override
   public void onBindRealmViewHolder(ViewHolder viewHolder, int position) {
       final Note note = realmResults.get(position);
       viewHolder.mText.setText(note.getText());
       viewHolder.mDate.setText(note.getDate().toString());
   }
}

This class looks a lot more complicated than it actually is but if you’ve used Adapters in Android before you’re probably feeling “at home”.

Our class takes a Context and a results object as arguments for its constructor. It then goes on to define a ViewHolder which just like the name suggests holds information about the view we’re trying to display.

The ViewHolder is based on the layout resource file we created earlier and called note_item.xml.

We then override two of its methods. One for when the view holder is created so we inflate our layout inside it and another one for when the view holder is already loaded and we know it’s safe for us to add data to it.

Now we just need to load the data from the database and pass it to our adapter.  Add a call to a method named loadNotes() to the bottom of the onCreate method in the MainActivityClass.

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   mRealmConfig = new RealmConfiguration
           .Builder(this)
           .build();
   Realm.setDefaultConfiguration(mRealmConfig);
   mRealm = Realm.getDefaultInstance();
   mText = (EditText) findViewById(R.id.et_text);
   mNotes = (RealmRecyclerView) findViewById(R.id.rv_notes);


   Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
   setSupportActionBar(toolbar);

   FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
   fab.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           if(mText.getText().length() > 0){
               insertNote(mText.getText().toString());
               mText.setText("");

               Snackbar.make(view, "Note added!", Snackbar.LENGTH_LONG)
                       .setAction("Action", null).show();
           }
       }
   });

   loadNotes();
}

Create a method below onCreate with that same name. It will be responsible for loading our notes and adding them into our view adapter:

private void loadNotes() {
   RealmResults<Note> notes = mRealm.where(Note.class).findAllSorted("date", Sort.ASCENDING);
   NoteRecyclerViewAdapter noteAdapter = new NoteRecyclerViewAdapter(getBaseContext(), notes);
   mNotes.setAdapter(noteAdapter);
}

We’re querying the database by asking it to return any entities that are stored in the database for our Notes model. We’re using the findAllSorted method without passing any filters. You can read more about querying a Realm database here.

Give the app another go and what you should see now is that the notes you created earlier are displayed in the list. Also try adding a new note and see how the RecyclerView gets refreshed automatically.

add-note.gif

Bonus

Through the RealmBasedRecyclerViewAdapter we also get some nice bonuses out of the box. You’ve already seen one of them, which is the capability of auto-refreshing, but try and swipe one of your notes left or right, and you’ll see it gets deleted from the database. We enabled this earlier when we created our layout and set app:rrvSwipeToDelete to true.

The last trick I want to show you is one that is useful when you have lots of notes and it becomes hard to find things. With the wrapper we’re using we have the option to add headers to the adapter so we can display something more organised like this:

device-headers.png

To get these headers working you need to change the class NoteRecyclerViewAdapter to be constructed with the following:

public NoteRecyclerViewAdapter(
   Context context,
   RealmResults<Note> realmResults) {
   super(context, realmResults, true, true, true, "text");
}

We’ve added two extra arguments to the end of the call to super. One specifies that we want to show headers and the second says that we want it to be ordered by the text property on our model.

You own the Realm

Now that we’ve looked at how to create a simple application using Realm as a database, how about moving on to a more complex application or using its true multi-platform strength across different applications?

The ease of use and ability to get a database up and running in minutes surely makes me want to build a lot more with it. I can’t wait for you to tell me what you’ve built. Hit me up on Twitter @marcos_placona or by email on marcos@twilio.com to tell me about it.