Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for opening/closing Realm instances

I have an Android app that uses a pretty common design pattern:

  1. The main activity is essentially presenting a list of objects - on small devices it does so by hosting a single fragment that displays a recyclerview of this list. On larger devices it hosts two fragments, one which has the same recyclerview of objects, and another which will host the detail for individual objects when one is selected in the list.
  2. On smaller devices, when a selection from the list is made, an activity is launched that hosts a fragment that utilizes a ViewPager to allow "swiping" through the list of objects, and edit each one in place.

In both cases, the user is allowed to edit only from the detail fragment.

I currently have my realm instance initialized in the application class, then the default instance retrieved in an activity base class I use to hold some housekeeping methods:

public abstract class SingleFragmentActivity extends AppCompatActivity {
    private Realm realm;
    protected abstract Fragment createFragment();

    @LayoutRes
    protected int getLayoutResId() {
        return R.layout.activity_fragment;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        realm = Realm.getDefaultInstance();
        // Initialize ProfileLab
        ProfileLab.get(realm);
        setContentView(getLayoutResId());

        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentById(R.id.fragment_container);

        if (fragment == null) {
            fragment = createFragment();
            fm.beginTransaction()
                    .add(R.id.fragment_container, fragment)
                    .commit();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if ( realm != null) {
            realm.close();
        }
    }
}

Note that I am storing this instance of realm in a static class "ProfileLab":

// Initialize ProfileLab
ProfileLab.get(realm);

Then in the various fragments that update data, I am doing stuff like:

mProfile = ProfileLab.get().getProfile(profileId);
*
* do CRUD activities here for example:
*
    private void deleteProfile() {
        ProfileLab.get().deleteProfile(mProfile);
        mCallbacks.onProfileUpdated(mProfile);
    }

Then in ProfileLab, it looks like:

public boolean deleteProfile(Profile c) { boolean retVal = true;

try {
    mRealm.beginTransaction();
    c.deleteFromRealm();

} catch (Exception e) {
    retVal = false;
} finally {
    if ( mRealm != null ) {
        if (retVal) {
            mRealm.commitTransaction();
        } else {
            mRealm.cancelTransaction();
        }

    }
}
return (retVal);

}

My question - is this a problem to essentially hold that Realm instance open like that throughout the use of the app? I noticed this paragraph in the docs:

If you get a Realm instance from a thread that does not have a Looper attached, then objects from such instance will not be updated unless the waitForChange() method is called. It is important to note that having to hold on to an old version of your data is expensive in terms of memory and disk space and the cost increases with the number of versions between the one being retained and the latest. This is why it is important to close the Realm instance as soon as you are done with it in the thread.

The thing is, I am not 'done with it' because this is on the UI thread, which is obviously running throughout the lifetime of my app.

I can't really open/close the realm instance just for the atomic updates, because I need to use the result of the initial query to show the list of objects from which to choose to edit - when I tried that initially (I had realm object open/close within each method in ProfileLab itself) I got an error in my recycler adapters that the realm had been closed...

The example code showing use of the recycler view shows realm being retrieved/used/closed at the individual activity level, if I do that between say the two simple activities (hosting the RecyclerView and hosting the ViewPager), will the data updates be reflected in each other?

like image 246
tfrysinger Avatar asked Feb 19 '17 00:02

tfrysinger


2 Answers

Opening and closing the realm within try/catch block is recommended. for an example:

try {
 Realm realm = Realm.getDefaultInstance();
 //Use the realm instance

}catch(Exception e){
  //handle exceptions
}finally {
    realm.close();
}

It's a basic example when going to use. If you can close within AsyncTask, it'll be better.

The official documentation refers that if you use minSdkVersion >= 19 and Java >= 7, you won't close it manually.

try (Realm realm = Realm.getDefaultInstance()) {
// No need to close the Realm instance manually
}
like image 168
Fahry Mohammed Avatar answered Nov 10 '22 09:11

Fahry Mohammed


Realm will automatically keep Realms on Looper threads up to date. That particular line in the documentation mostly refers to background threads. So your code is fine, even if onDestroy might not be called.

You can also read these relevant sections in the docs:

https://realm.io/docs/java/latest/#closing-realms https://realm.io/docs/java/latest/#realm-instance-lifecycle

like image 1
Christian Melchior Avatar answered Nov 10 '22 09:11

Christian Melchior