This is a rewrite of a "Realm tutorial" on Android Hive. Unfortunately the tutorial is extremely outdated (uses 0.82.1 even though the version 3.5.0 is out!), the code is unstructured (Realm transactions inside a click listener inside a dialog created in a long click listener); and it also misuses Realm quite heavily:
begin/commitTransaction()
instead of executeTransaction()
refresh()
even though the Realm instance is freshly openIt also uses outdated practices or is just not up-to-date information:
refresh()
doesn't even exist anymore, and even when it did, in this use-case it was not neededinitialData()
exists nownull
support for primitives isn't in, even though it was added in 0.83.0commitTransaction()
immediately updating the RealmResults<T>
and calling adapter.notifyDataSetChanged()
manually, but that's not the case since 0.89.0 which means you need to add a change listener to the RealmResults<T>
(which RealmRecyclerViewAdapter
does for you automatically)So with that in mind, this repository shows how to do these things right:
executeTransactionAsync()
on the UI threadinitialData()
to prepopulate the RealmRealmManager
class (a bit stub-like because I'll have to make its content not static later) to manage number of open activitiesOnClickListener
s)Application
subclass explicitly because of Firebase Crash Reporting for example creating multiple Application instancesRealmRecyclerViewAdapter
with asynchronous querySo yeah, this is the interesting class:
public class RealmManager {
static Realm realm;
static RealmConfiguration realmConfiguration;
public static void initializeRealmConfig(Context appContext) {
if(realmConfiguration == null) {
setRealmConfiguration(new RealmConfiguration.Builder(appContext).initialData(new RealmInitialData())
.deleteRealmIfMigrationNeeded()
.build());
}
}
public static void setRealmConfiguration(RealmConfiguration realmConfiguration) {
RealmManager.realmConfiguration = realmConfiguration;
Realm.setDefaultConfiguration(realmConfiguration);
}
private static int activityCount = 0;
public static Realm getRealm() {
return realm;
}
public static void incrementCount() {
if(activityCount == 0) {
if(realm != null) {
if(!realm.isClosed()) {
realm.close();
realm = null;
}
}
realm = Realm.getDefaultInstance();
}
activityCount++;
}
public static void decrementCount() {
activityCount--;
if(activityCount <= 0) {
activityCount = 0;
realm.close();
Realm.compactRealm(realmConfiguration);
realm = null;
}
}
}
Which has its RealmConfiguration
initialized in Activity.onCreate()
, and the Realm instance itself is opened with RealmManager.incrementCount()
from the retained fragment's constructor.
public class BooksScopeListener extends Fragment {
BooksPresenter booksPresenter;
public BooksScopeListener() {
setRetainInstance(true);
RealmManager.incrementCount();
booksPresenter = new BooksPresenter();
}
@Override
public void onDestroy() {
RealmManager.decrementCount();
super.onDestroy();
}
public BooksPresenter getPresenter() {
return booksPresenter;
}
}
Which is created in the Activity.
@Override
protected void onCreate(Bundle savedInstanceState) {
RealmManager.initializeRealmConfig(getApplicationContext());
super.onCreate(savedInstanceState);
BooksScopeListener fragment = (BooksScopeListener) getSupportFragmentManager().findFragmentByTag("SCOPE_LISTENER");
if(fragment == null) {
fragment = new BooksScopeListener();
getSupportFragmentManager().beginTransaction().add(fragment, "SCOPE_LISTENER").commit();
}
realm = RealmManager.getRealm();
booksPresenter = fragment.getPresenter();
The adapter is set up like this
recycler.setAdapter(new BooksAdapter(this, realm.where(Book.class).findAllAsync(), booksPresenter));
Where the adapter is a proper RealmRecyclerViewAdapter
:
public class BooksAdapter extends RealmRecyclerViewAdapter<Book, BooksAdapter.BookViewHolder> {
And the writes are from the UI thread to a background thread using executeTransactionAsync()
, found in the presenter.
Realm realm = RealmManager.getRealm();
realm.executeTransactionAsync(new Realm.Transaction() {