(I've seen other posts on this but they're a bit outdated, mostly referencing realm 1.x)
Been looking for the best way to institute realm in our app. We're currently all over the place and looking to centralize our realm instance.
In this link (https://medium.com/#Zhuinden/basics-of-realm-a-guide-to-using-realm-1-2-0-634471c0fe8f), I found what I think is a great global implementation but I have 2 questions on it:
1. Does this still apply to current versions of realm (currently at 4.1)?
2. What's the purpose of the retained fragment and can it be done without one?
Thanks in advance!
Here's the relevant section from the link above:
- Making a global Realm instance
I set up a retained fragment in my activity which increments the current active > Activity count. Open the
Realm if the counter went from 0 to 1. Close the Realm if the counter
went from 1 to 0.
import android.content.Context;
import android.util.Log;
import io.realm.Realm;
import io.realm.RealmConfiguration;
/**
* Created by Zhuinden on 2016.08.16..
*/
public class RealmManager {
static Realm realm;
static RealmConfiguration realmConfiguration;
public static void initializeRealmConfig(Context appContext) {
if(realmConfiguration == null) {
setRealmConfiguration(new RealmConfiguration.Builder(appContext)
.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 = Realm.getDefaultInstance();
}
activityCount++;
}
public static void decrementCount() {
activityCount--;
if(activityCount <= 0) {
activityCount = 0;
realm.close();
Realm.compactRealm(realmConfiguration);
realm = null;
}
}
}
The setRealmConfiguration() method is so that you can replace the
default configuration from instrumentation tests with a config for an
inMemory() Realm. If you use this, then you can easily count activity
references using a retained fragment!
public class BooksScopeListener extends Fragment { // could go to base class
public BooksScopeListener() {
setRetainInstance(true);
RealmManager.incrementCount();
}
#Override
public void onDestroy() {
RealmManager.decrementCount();
super.onDestroy();
}
}
public class BooksActivity extends AppCompatActivity {
Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
RealmManager.initializeRealmConfig(getApplicationContext()); // could go to base class
super.onCreate(savedInstanceState);
BooksScopeListener fragment = (BooksScopeListener) getSupportFragmentManager().findFragmentByTag("SCOPE_LISTENER"); // could go to base class
if(fragment == null) {
fragment = new BooksScopeListener();
getSupportFragmentManager().beginTransaction().add(fragment, "SCOPE_LISTENER").commit();
}
//get realm instance
realm = RealmManager.getRealm();
Ta-dah, now you can freely access RealmManager.getRealm() anywhere for
the UI thread. Or just #Inject this from a module. (Don’t forget that
the scoped approach per activity or fragment works too, I just prefer
this one because I used #Singleton component over #Subcomponents).
I wrote that article about a year ago, and while that creates a global Realm for the UI thread, you won't be able to just use it on any thread. And if you open/close the Realm on background threads, you still need to pass Realm as method argument.
Does this still apply to current versions of realm (currently at 4.1)?
Realm lifecycle management is still a thing that you need to do, and the same principle would apply even for 4.1.0, just like it did in 1.2.0. This is one thing that never really changed :)
What's the purpose of the retained fragment and can it be done without one?
The retained fragment is added so that you don't close/reopen the Realm just because you rotated the screen; lately you can do this with ViewModel's constructor + onCleared() method as well.
I just found that retained fragments are the trustworthiest way of listening to the lifecycle without configuration change messing with the lifecycle callbacks.
The RealmManager shown in the article is responsible solely for the automatic lifecycle management on the UI thread. If used on the UI thread, it still works fine. In fact, that's still where I'd call open/close from in the following example, for the UI thread, anyways.
If you want a singleton manager class for Realms across threads, a starting out point would be to use following class (a bit proof of concept as I haven't used it in production yet, but hey):
public class RealmManager {
private final ThreadLocal<Realm> localRealms = new ThreadLocal<>();
/**
* Opens a reference-counted local Realm instance.
*
* #return the open Realm instance
*/
public Realm open() {
checkDefaultConfiguration();
Realm realm = Realm.getDefaultInstance();
if(localRealms.get() == null) {
localRealms.set(realm);
}
return realm;
}
/**
* Returns the local Realm instance without adding to the reference count.
*
* #return the local Realm instance
* #throws IllegalStateException when no Realm is open
*/
public Realm get() {
Realm realm = localRealms.get();
if(realm == null) {
throw new IllegalStateException(
"No open Realms were found on this thread.");
}
return realm;
}
/**
* Closes local Realm instance, decrementing the reference count.
*
* #throws IllegalStateException if there is no open Realm.
*/
public void close() {
checkDefaultConfiguration();
Realm realm = localRealms.get();
if(realm == null) {
throw new IllegalStateException(
"Cannot close a Realm that is not open.");
}
realm.close();
// noinspection ConstantConditions
if(Realm.getLocalInstanceCount(Realm.getDefaultConfiguration()) <= 0) {
localRealms.set(null);
}
}
}
This way it is actually possible to use RealmManager class as single point of entry provided as #Provides #Singleton RealmManager realmManager(), and detach open() from get() thus allowing you to use an open Realm instance for a given thread (if it is already open), without risk of never being able to close it (because of calling Realm.getDefaultInstance() without a close() pair).
Then for background thread, you'd use
try {
realmManager.open();
...
} finally {
realmManager.close();
}
And in other methods inside you can do
Realm realm = realmManager.get();
As for UI thread, you'd use the retained fragment (or Activity/Fragment onCreate()/onDestroy()) as you normally would, but using open/close as well.
This way you can manage the Realm lifecycle in a single place, but still pass Realm as thread-local variable, instead of a global one (which would not work, as Realm instances are thread-confined).
Related
I have kept a single realm instance opened on main thread in Application class and I use that single instance to do all kinds of DB operations from MainActivity. Since my application has a single activity, I close the instance in the activity's onDestroy(). The app is working fine for me as of now.
What are the repercussions of not doing a realm.close()? My database hasn't corrupted with or without the same.
Also, I've read that there are scenarios in which the Activity's onDestroy() may not get called at all. What effects the database can have in such a scenario if closing realm is so important?
public class MyApp extends Application {
private static MyApp instance;
private Realm realm;
public void onCreate() {
super.onCreate();
Realm.init(this);
Realm.setDefaultConfiguration(new RealmConfiguration.Builder()
.schemaVersion(BuildConfig.VERSION_CODE)
.migration(new RealmMigrationClass())
.compactOnLaunch()
.build());
realm = Realm.getInstance(Realm.getDefaultConfiguration());
}
public static MyApp getInstance() {
return instance;
}
public Realm getRealm() {
return realm;
}
}
MainActivity
public class MainActivity extends Activity {
#Override
protected void onDestroy() {
MyApp.getInstance().getRealm().close();
super.onDestroy();
}
}
Closing the realm instance is very important because of realm core has been written in c++ programming language and is compiled in the native code.And we know the c++ garbage collection does not run automatically we require to manually call the garbage collection.So when you call the realm.close() it means that realm deallocation the native memory means free or delete the pointer variable and also do the file descriptor job.From realm.close() means you give the command or tell to native c++ compiler to run the garbage collection.
If you look the "doc" (REALM_DOC) for Realm for Java you can find:
Realm implements Closeable to take care of native memory deallocation
and file descriptors, so always close your Realm instances when you’re
done with them.
Realm instances are reference counted—if you call getInstance twice in
a thread, you need to call close twice as well. This allows you to
implement Runnable classes without having to worry about which thread
will execute them: simply start it with getInstance and end it with
close.
Personally I suggest you to define a class in which define your Realm functions and an "Realm attribute" (like a "RealmHelper" class) then inside this class define:
- a unstatic Realm
- a static RealmHelper instance
You will always use this RealmHelper static instance for all operations in your Realm inside your main Thread, inside other threads you will call "new RealmHelper()" and CLOSE the realm just after you did the operation.
Doing this in your MainThread you just need to close ONE realm instance when the application get closed, to do this you can use the "Application.ActivityLifecycleCallbacks" interface inside a Custom defined Application class (so which extends Application of Android).
Example inside you Application custom class:
/* START Override ActivityLifecycleCallbacks Methods */
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
#Override
public void onActivityStarted(Activity activity) {
// Check if your MyRealmClass instance is null or is closed, in this case
// re-initialize it.
if(MyRealmClass.getInstance() == null || MyRealmClass.getInstance().getRealm().isClosed()){
MyRealmClass.initInstance();
}
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
if(!AppUtils.isAppOnForeground(this)){
// Close your MyRealmClass instance
if(MyRealmClass.getInstance() != null) {
MyRealmClass.getInstance().close();
MyRealmClass.getInstance().logRealmInstanceCount(LABEL_APP_IN_BACKGROUND);
MyRealmClass.setMyInstance(null);
}
}
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
/* END Override ActivityLifecycleCallbacks Methods */
Code of "isAppOnForeground" (check if your app is in foreground, if is not this mean your app is being closed):
public static boolean isAppOnForeground(Context context) {
boolean ret = false;
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if(appProcesses != null){
String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
ret = true;
}
}
}
return ret;
}
Your "MyRealmClass" will look like:
public class MyRealmClass {
protected Realm mRealm;
protected static MyRealmClass mInstance;
public MyRealmClass() {
mRealm = Realm.getDefaultInstance();
}
public static MyRealmClass initInstance(){
if(mInstance == null){
mInstance = new MyRealmClass();
}
return mInstance;
}
public static MyRealmClass getInstance(){
return mInstance;
}
public static void setMyInstance(MyRealmClass instance) {
mInstance = instance;
}
public Realm getRealm() {
return mRealm;
}
public void setRealm(Realm realm){
this.mRealm = realm;
}
public void close() {
if (mRealm != null) {
try {
mRealm.close();
} catch(Exception e){
onException(e);
}
}
}
[...]
Then you need to check that all your Realm instance is not closed when you use a RealmObject or you do some operation in your Realm. And if it is closed (because the app got in background and then restarted) you need to re-initialize the realm (if you have an activity with a MyRealmClass instance as attribute).
Example in a BaseMyActivity:
public abstract class MyBaseActivity extends AppCompatActivity {
protected MyRealmClass mRealmClass;
/* START Override Lifecycle Methods */
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initMyRealmClass();
Lyra.instance().restoreState(this, savedInstanceState);
}
#Override
protected void onStart() {
super.onStart();
initMyRealmClass();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Lyra.instance().saveState(this, outState);
}
/* END Override Lifecycle Methods */
/* START Private Methods */
protected void initMyRealmClass(){
if(mRealmClass == null || mRealmClass.getRealm().isClosed()){
mRealmClass = MyRealmClass.initInstance();
}
}
/* END Private Methods */
}
Basically all your activities will extend this BaseActivity if they need to use Realm functions. (Lyra is used to save the state of any of your attributes: LYRA)
REMEMBER THAT:
if you set or get some attributes from a RealmObject or you get an object from a RealmList or RealmResults you NEED THAT YOUR REALM INSTANCE, from which the object was take, IS OPEN.
OTHERWISE you need to use this method when you init a variable with objects from the realm: (this methods should be placed in yuour "MyRealmClass")
public <T extends RealmObject> List<T> toList(RealmResults<T> results) {
return mRealm.copyFromRealm(results);
}
public <T extends RealmObject> List<T> toList(RealmList<T> results) {
return mRealm.copyFromRealm(results);
}
public <T extends RealmObject> T copyObjectFromRealm(T obj) {
return mRealm.copyFromRealm(obj);
}
public <T extends RealmObject> RealmResults<T> findAllObject(Class<T> classObject) {
RealmQuery<T> query = mRealm.where(classObject);
return query.findAll();
}
Now if you need to get a List of "MyRealmObjectClass" objects and add them to an adapter you will do this:
List<MyRealmObjectClass> myObjects = mRealmClass.toList(mRealmClass.findAllObject(MyRealmObjectClass.class))
myAdapter.addAll(myObjects);
Doing this if you "get" or "set" an attribute after the Realm instance, from which you got the objects, was closed (for example after the app get to background and then restarted) you won't get an exception.
BUT if you "set" an attribute of your RealmObject this WON'T BE SET in the REALM INSTANCE, so to change the value of a RealmObject inside the Realm in this case you need to Save the object!
OTHERWISE if you have a RealmResults or a RealmObject which is still connected to the Realm, so you can directly change, inside a transaction, an attribute of it and it will be changed inside the Realm too.
To do a Realm Transaction I suggest you to follow the DOC in the first link and, if you don't need to close the Realm in the Finally block, enable lambda and do this:
mRealm.executeTransaction(
realm -> {
[do your Realm operations]
}
)
or you can also do:
public boolean doRealmOperation(Object... params){
AtomicBoolean ret = new AtomicBoolean(false);
mRealm.executeTransaction(
realm -> {
try{
[do your realm operation]
ret.set(true);
} catch(Exception e){
onException(e)
ret.set(false);
}
}
)
}
in this case you need to use the "AtomicBoolean" because you will set the value you want to return inside the transaction, but inside a transaction the value got from outside of the transaction itself (in this case the "ret" variable) MUST BE A FINAL variable. But you can't define "ret" as "final" and then set it again, so you need to use the "AtomicBoolean" to set the variable outside the transaction and set it again inside the transaction itself.
(You can also avoid this problem by using a temporary variable to get the "true/false" value inside the transaction and then set the "ret" variable using that "temp variable". But personally I prefer to use "AtomicBoolean" class which is, I think, safer and more clean than a temp variable)
Hope this is helpful,
see you by and happy coding! ;)
Realm implements Closeable to take care of native memory deallocation and file descriptors, so always close your Realm instances when you’re done with them.
Realm instances are reference counted—if you call getInstance twice in a thread, you need to call close twice as well.
From my personal experience not closing realm has not caused a lot of issues, in fact when I tried closing it at times it would cause an issue when the app went into the background and was then resumed which caused a crash due to realm instance being closed, I am not sure why a new instance of realm was not created in that case, might have been a bug.
As of now I follow the realm docs and close my realm instances until they cause an issue.
General coding practises suggest that anything that is opened should be safely closed.
Yes, it will get closed only if you called close() method on your application's destroy() method. Remember Realm implements Closeable in order to take care of native memory deallocation and file descriptors so it is important to close your Realm instances when you are done with them.
For further info visit this link.
I'm using Dagger 2 to provide a singleton Realm instance for the whole app (all data access objects use a single realm). However, as far as I know, Realm can have multi-instance using Realm.getInstance() and we have to close each instance when we're done with it as presented by the Realm docs:
/**
* Closes the Realm instance and all its resources.
* <p>
* It's important to always remember to close Realm instances when you're done with it in order not to leak memory,
* file descriptors or grow the size of Realm file out of measure.
*
* #throws IllegalStateException if attempting to close from another thread.
*/
#Override
public void close() {
if (this.threadId != Thread.currentThread().getId()) {
throw new IllegalStateException(INCORRECT_THREAD_CLOSE_MESSAGE);
}
if (realmCache != null) {
realmCache.release(this);
} else {
doClose();
}
}
My question is: should I use a singleton Realm instance as I did, or create a realm instance for each Activity / Fragment and close it using realm.close() at onDestroy()?
Managed RealmObjects (which are lazy-loaded on their access) are accessible only if there is at least 1 open instance of Realm on that given thread, however NOT closing Realm instance on non-looper background thread is very severe problem.
If you provide thread-local singleton Realm from Dagger module then that Realm instance will only be accessible on the thread it was created on. And will cause a crash accessed from anywhere else.
One possibility would be to provide a singleton class of your own that can open Realm instances, like this:
#Singleton
public class RealmManager {
private final ThreadLocal<Realm> localRealms = new ThreadLocal<>();
#Inject
public RealmManager() {
}
/**
* Opens a reference-counted local Realm instance.
*
* #return the open Realm instance
*/
public Realm openLocalInstance() {
checkDefaultConfiguration();
Realm realm = Realm.getDefaultInstance(); // <-- maybe this should be configurable
if(localRealms.get() == null) {
localRealms.set(realm);
}
return realm;
}
/**
* Returns the local Realm instance without adding to the reference count.
*
* #return the local Realm instance
* #throws IllegalStateException when no Realm is open
*/
public Realm getLocalInstance() {
Realm realm = localRealms.get();
if(realm == null) {
throw new IllegalStateException(
"No open Realms were found on this thread.");
}
return realm;
}
/**
* Closes local Realm instance, decrementing the reference count.
*
* #throws IllegalStateException if there is no open Realm.
*/
public void closeLocalInstance() {
checkDefaultConfiguration();
Realm realm = localRealms.get();
if(realm == null) {
throw new IllegalStateException(
"Cannot close a Realm that is not open.");
}
realm.close();
// noinspection ConstantConditions
if(Realm.getLocalInstanceCount(Realm.getDefaultConfiguration()) <= 0) {
localRealms.set(null);
}
}
private void checkDefaultConfiguration() {
if(Realm.getDefaultConfiguration() == null) {
throw new IllegalStateException("No default configuration is set.");
}
}
}
But even then, you'd need to manage the local instances for the given threads where you need them.
public class MainActivity
extends AppCompatActivity {
RealmManager realmManager;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
realmManager = Injector.get().realmManager();
realmManager.openLocalInstance();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
...
}
#Override
protected void onDestroy() {
super.onDestroy();
realmManager.closeLocalInstance();
}
A singleton instance most likely gets you into problems. You should create one for each Activity/Fragment instead.
I used to work with un-auto-managed model objects that were copied from Realm. I switched to using auto-managed Realm object for my new projects and have a problem.
Where before I could keep my DB logic separate, in DAO classes, right now, the Realm code is all over my application.
Realm objects should be managed (closed) well, everywhere, on every thread, on every activity and fragment. But what I dislike most: every setter on every model object needs to be in a Realm transaction. There is Realm all over my code right now!
Did anyone find a way to keep the DB logic somewhat separate, while using auto managed Realm objects?
Conclusion, after a few months with Realm auto-managed objects:
Having your database code centralised, in Data Access Object (DAO) classes or the like, is hardly possible when using Realm’s auto-updated objects.
Every setter on every model object needs to be inside a Realm transaction block. If you use RetroLambda, the calls are relatively clean:
realm.executeTransaction(r -> user.setFirstName(firstName));
These blocks will be all over your project within no time. Forgetting to wrap a setter method (or a constructor call) in a transaction will crash your app. Believe me, this will happen a lot in the first weeks of adopting auto-updated objects.
Auto-updated objects cannot be shared across threads. The same is true for Realm instances.
All threads, and often activities and fragments, will have to open and close Realm instances. You’ll be thinking constantly “On which thread am I?”. If you cross thread boundaries with model objects or Realm instances, you will crash your app.
More info here: https://medium.com/#ffvanderlaan/realm-auto-updated-objects-what-you-need-to-know-b2d769d12d76
and here: https://medium.com/#Zhuinden/how-to-use-realm-for-android-like-a-champ-and-how-to-tell-if-youre-doing-it-wrong-ac4f66b7f149#.gazrajqwt
What I did was created a RealmController class that handles all of my Realm transactions, and fetches new data from the API. The skeleton for the implementation is borrowed from another SO post which I can't find right now, but you should be able to find it on Google by searching RealmController.
public class RealmController {
private static RealmController instance;
private final Realm realm;
private ServiceInterface restInterface;
private final String TAG = "RealmController";
private static boolean browseFetchAllowed = true;
private RealmConfiguration config = new RealmConfiguration.Builder()
.name("myRealm")
.schemaVersion(0)
.deleteRealmIfMigrationNeeded()
.build();
public RealmController(Application application) {
restInterface = ServiceGenerator.createService(ServiceInterface.class);
realm = Realm.getInstance(config);
}
public void setAuthentication(String token, String uid) {
this.restInterface = ServiceGenerator.createService(ServiceInterface.class, token, uid);
}
public static RealmController with(Fragment fragment) {
if (instance == null) {
instance = new RealmController(fragment.getActivity().getApplication());
}
return instance;
}
public static RealmController with(Activity activity) {
if (instance == null) {
instance = new RealmController(activity.getApplication());
}
return instance;
}
public static RealmController with(Application application) {
if (instance == null) {
instance = new RealmController(application);
}
return instance;
}
public RealmController getInstance() {
return instance;
}
public ServiceInterface getRestInterface() {
return restInterface;
}
public Realm getRealm() {
return realm;
}
public RealmResults<MyRealmObject> getStuff() {
return realm.where(MyRealObject.class).findAll();
}
public void setStuff(RealmList<MyRealmObject> stuff) {
realm.beginTransaction();
realm.copyToRealmOrUpdate(stuff);
realm.commitTransaction();
}
public void getStuffFromServer() {
restInterface.getStuff().enqueue(new Callback<RealmList<MyRealmObject>>() {
#Override
public void onResponse(Call<RealmList<MyRealmObject>> call, Response<RealmList<MyRealmObject>> response) {
if (response.isSuccessful()) {
realm.beginTransaction();
realm.copyToRealmOrUpdate(response.body);
realm.commitTransaction();
}
}
#Override
public void onFailure(Call<RealmList<RealmAd>> call, Throwable t) {
t.printStackTrace();
}
});
}
}
Towards the bottom of the file I've added a few examples, but usage is quite simple:
RealmResults<MyRealmObject> results = RealmController.with(this).getStuff();
In terms of having the objects auto-managed, what I've been using is RealmResults, which is always auto-managed (even if you're querying for a single object). Then just add a change listener to the results of the query and voilà. Also, if you want to have auto-updating data in recyclerviews, I would recommend RealmRecyclerView.
My project using clean architecture. In this situation, the UI layer is separate from Domain layer. So I think it would be better the UI layer doesn't own realm instance. As realm's doc recommend managing the realm instance in Activity's lifecycle, how should I deal with the realm instance then?
To be more clear, my project is too heavy to change all objects extends RealmObject. So I use separate object to persistent data. When the api call finish, a business object convert to a realm object, opposite when query from realm. I create the method like this:
public void insert(T object){
final Realm realm = RealmProvider.getRealm();
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.copyToRealmOrUpdate(createRealmObject(object));
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
realm.close();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
realm.close();
}
});
}
Actually, it works fine. But below I don't know how to handle closing realm instance.
public Observable<T> queryAsync(Condition<? extends RealmObject> condition) {
final Realm realm = RealmProvider.getRealm();
return condition.getQuery(realm).findFirstAsync()
.asObservable()
.filter(new Func1<RealmObject, Boolean>() {
#Override
public Boolean call(RealmObject realmObject) {
return realmObject.isLoaded();
}
})
.map(new Func1<RealmObject, T>() {
#Override
public T call(RealmObject realmObject) {
return createObjectFromRealm(realmObject);
}
});
}
If you want a clean separation between UI and database layers in your code, and you want to abstract away your database logic so that ideally your activity can call database layer without knowing how that layer is implemented, then Realm is probably not what you're looking for.
Realm objects are tied to realm instances which means that if you retrieve an object from a realm instance and then close that instance (which you must), you can no longer use the object. Which defeats the entire purpose of using Realm.
If you are going to use Realm, you should keep the realm logic closely tied to your activities/services etc, and don't try to hide it in a separate layer, so that you have full control over it.
.map(new Func1<RealmObject, T>() {
#Override
public T call(RealmObject realmObject) {
Object o = createObjectFromRealm(realmObject);
realm.close();
return o;
}
});
One of the major aspect of a clean architecture is, isolation of major libraries (i.e. Realm). Since Realm, RealmObject, RealmResults are not accessible outside of the Thread they are created in, it makes it even more important to keep Realm & Realm related calculations isolated from rest of the code.
You are using RxJava in your queryAsync() method, and at the same time you are using executeTransactionAsync() method, which defies the whole purpose of using RxJava. You could have done like this,
public void insert(T object){
final Realm realm = RealmProvider.getRealm();
realm.executeTransaction(realm1 ->
realm1.copyToRealmOrUpdate(createRealmObject(object)));
realm.close();
}
In a good Architecture, for each jsonModel class there should be a corresponding realmModel class & a DAO (Data Access Object). DAO class must take jsonModel as argument and must return jsonModel as result. All Realm related operations must be restricted within the DAO file, that way none of the code other than DAO and realmModel knows about Realm.
Here is an article about Realm best practices with a good architechture https://medium.com/#Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f
Also a sample project demonstrating Integration of Realm on Android with MVP(Model View Presenter), RxJava, Retrofit, Dagger, Annotations & Testing.
https://github.com/viraj49/Realm_android-injection-rx-test
I was trying to avoid creating and managing Realm objects, in my android application, for every fragment. I am thinking ThreadLocalVariable might be a good start.
public class RealmInstanceGenerator extends ThreadLocal<Realm> {
public Realm getRealmForMyThread(Context context) {
if(get() == null && context != null)
super.set(Realm.getInstance(context));
return get();
}
public void setRealmForCurrentThread(Context context) {
if(context != null)
super.set(Realm.getInstance(context));
}
#Override
protected Realm initialValue() {
return null;
}
#Override
public void remove() {
if(get() != null) get().close();
super.remove();
}
}
I would just create a static final object of RealmInstanceGenerator in my utils singleton class and call setRealmForCurrentThread in my MainActivity. Then I will call remove when my activity dies. For any new thread a new Realm object will be automatically generated.
Is it a good strategy ?
Christian from Realm here.
It is a good strategy, and luckily we already implemented it for you :) All Realm instances are already being cached in a ThreadLocal and we keep track of instances using a counter. The Realm is only fully closed once the counter reaches 0.
This means as long as you always calls close() (which you should), it is effectively the same as your remove() method.
You can see the pattern used in this example here: https://github.com/realm/realm-java/tree/master/examples/threadExample/src/main/java/io/realm/examples/threads
And the source code for the Realm class is here: https://github.com/realm/realm-java/blob/master/realm/src/main/java/io/realm/Realm.java