Realm in Android – simple example
What is Realm?
Realm is another type of database in Android. But, what is very important, Realm doesn’t use SQLite. If you use ORMLite or ActiveAndroid or any other similar library, your data is stored in SQLite database, because these libraries give us only an overlay on SQLite. With Realm there is no SQLite at all. Thanks to that Realm is very fast and you are even allowed to write the data in UI thread (mostly).
Gradle
Add the following line to your dependencies.
1 2 3 4 | dependencies { compile 'io.realm:realm-android:0.87.4' //other dependencies } |
Simple example
I’ve created very simple project to show how the database works and how to use it. Application consists of one Activity (container) and two Fragments. First Fragment allows us to add a book which is stored in database (MyEditionFragment) and the second one shows a list of all books from the database (MyListFragment). And that’s it.
RealmObject
To use your object as a Realm object you have to simply extend RealmObject class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class MyBook extends RealmObject { @Required private String title; public String getTitle() { return title; } public void setTitle(final String title) { this.title = title; } } |
Annotation @Required means that title cannot be null.
CRUD operations
All of the CRUD operations are made with the use of Realm instance. How?
1 2 3 4 5 6 7 | private Realm mRealm; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRealm = Realm.getInstance(getContext()); } |
2. Do your operations on database. For example add or remove a book.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @OnClick(R.id.button_add) public void onAddClick() { mRealm.beginTransaction(); MyBook book = mRealm.createObject(MyBook.class); book.setTitle(getTrimmedTitle()); mRealm.commitTransaction(); } @OnClick(R.id.button_remove) public void onRemoveClick() { mRealm.beginTransaction(); RealmResults books = mRealm.where(MyBook.class).equalTo("title", getTrimmedTitle()).findAll(); if(!books.isEmpty()) { for(int i = books.size() - 1; i >= 0; i--) { books.get(i).removeFromRealm(); } } mRealm.commitTransaction(); } private String getTrimmedTitle() { return mEditTitle.getText().toString().trim(); } |
Each operation on database which is not the read operation, has to start with Realm.beginTransaction() and to end with Realm.commitTransaction(). There are two arguments for that – our data will always be in consistent state and it also ensures the thread safety. If you forget to commit your changes, they will not be saved. You can also cancel the transaction by calling Realm.cancelTransaction().
3. According to Realm documentation it is important to close all of the Realm instances when we are done with them.
1 2 3 4 5 | @Override public void onDestroy() { super.onDestroy(); mRealm.close(); } |
Auto-update magic
Above example shows how to add or remove some object in Realm but the real magic happens in the MyListFragment – the one with the list of books.
So what we are trying to do is to update our list when a new book is added or existing one is removed.
1. Get a Realm instance in Fragment.onCreate() and close it in Fragment.onDestroy().
2. Pass all of the books to adapter.
1 2 3 4 5 6 | @Override public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); mRecyclerView.setAdapter(new MyListAdapter(mRealm.allObjects(MyBook.class))); } |
Query Realm.allObjects(ClassName.class) returns RealmResults objects. It is very important to understand that the data in Realm is never copied. We get a list of references and we work on a original objects.
3. Add a RealmChangeListener to RealmResults.
1 2 3 4 5 6 7 8 9 | public MyListAdapter(RealmResults books) { mBooks = books; mBooks.addChangeListener(this); } @Override public void onChange() { notifyDataSetChanged(); } |
And here is where the magic happens. If we add a new book in MyEditionFragment, list in MyListFragment will refresh automatically. All we did is we added a listener which will inform us when the data, to be more precise – the books set, is changed. This is all it gives us and this is all we need. Method onChange() has no object (or list of objects) which was changed because we don’t need that information. We already have it. As it was said above, Realm data is never copied. So when a new book is added in MyEditionFragment, the list in MyListFragment adapter will be informed that something in books set was changed but mBooks object has been already automagically updated by Realm. All we have to do now is to update the view.
Troubleshouting
Hungarian notation
If you use the ‘m’ prefix in your class fields be aware that getters and setters should also have the ‘m’ character in their names. Read more.
Schema migration
During development database schema is changing all the time. If you have some data stored in database any future schema changes will cause a migration problems. You can either handle the migration changes or delete .realm file with all the data if Realm detects migration problems.
1 2 3 | RealmConfiguration config = new RealmConfiguration.Builder(context) .deleteRealmIfMigrationNeeded() .build() |
The project can be found on DroidsOnRoids Github in ExampleRealm-Android repository.
Ready to take your business to the next level with a digital product?
We'll be with you every step of the way, from idea to launch and beyond!
You need to make sure that you ALWAYS cancel open transactions in case of an exception. A transaction needs to be either cancelled or committed.
Yes, you’re definitely right. It is very important to remember that transaction has to be closed. Not calling cancel or commit when we ended our operations on database could cause an unexpected behavior in our model objects.
Yes, you’re definitely right. It is very important to remember that transaction has to be closed. Not calling cancel or commit when we ended our operations on database could cause an unexpected behavior in our model objects.
nice blog