Realm IllegalStateException - android

I'm trying to use realm as a persistence method of the downloaded data from an API. I'm getting the error: *
Caused by: java.lang.IllegalStateException: This Realm instance has
already been closed, making it unusable.
and I don't know what I'm doing wrong with the persistence methods. My class for persistence is the next:
public class PersistenceManager {
#Inject
public PersistenceManager(){}
public void saveHabitants(final List<Habitant> habitants){
Realm realm = Realm.getDefaultInstance(); // opens db
try {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.insertOrUpdate(habitants);
}
});
} finally {
realm.close();
}
}
public void saveHabitant(final Habitant habitant){
Realm realm = Realm.getDefaultInstance();
try {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.insertOrUpdate(habitant);
}
});
} finally {
realm.close();
}
}
public List<Habitant> loadHabitants(){
Realm realm = Realm.getDefaultInstance();
List<Habitant> results;
try {
results = realm.where(Habitant.class).findAll();
} finally {
realm.close();
}
return results;
}
public List<Habitant> loadHabitants(int indexFrom, int limit){
Realm realm = Realm.getDefaultInstance();
List<Habitant> results;
try {
results = realm.where(Habitant.class).findAll();
results.subList(indexFrom, limit);
} finally {
realm.close();
}
return results;
}
public Habitant loadHabitant(long id){
Realm realm = Realm.getDefaultInstance();
Habitant result;
try {
result = realm.where(Habitant.class).equalTo("id", id).findFirst();
} finally {
realm.close();
}
return result;
}
}
When I used the method loadHabitants(int indexFrom, int limit) is when I get this error.

A RealmResults is accessible through lazy loading (per accessor) for as long as there is at least 1 open Realm instance on the given thread.
I actually intended to refer to the documentation, but for some reason I couldn't find it? Anyways, a RealmResults is accessed lazily, so when there are no open Realm instances, it becomes invalid, and can no longer be accessed. This is true for RealmObjects as well.
The docs do say:
...RealmObjects and RealmResults are accessed through a lazy cache...
so you cannot return a managed RealmResults, and expect it to remain open, if you close the thread-local Realm instance it is associated with.
Two solutions:
1.) refer to the documentation on how to properly manage Realm lifecycle on UI thread and background thread
2.) use realm.copyFromRealm(), which eagerly evaluates the whole data set, creates a detached copy of it. Please note that this is not a free operation, larger data sets can easily cause UI thread to freeze up if you use copyFromRealm() on UI thread.
public List<Habitant> loadHabitants(int indexFrom, int limit){
try(Realm realm = Realm.getDefaultInstance()) {
List<Habitant> results = realm.where(Habitant.class).findAll();
results = new ArrayList<>(results.subList(indexFrom, limit));
for(int i = 0, size = results.size(); i < size; i++) {
results.set(i, realm.copyFromRealm(results.get(i)));
}
return Collections.unmodifiableList(results);
}
}

Related

Realm with Singleton Pattern in Android: Pros and Cons

Hi I have gone through the best practices in Realm and according to it the best way to handle Realm instances is to open a realm instance in onCreate() method of an Activity using relam = Realm.getDefaultInstance() and close it in onDestroy() using realm.close().
But currently I have following singleton structure in my code. I need to know the pros and cons of the following singleton structure over the best practice suggested in realm docs.
Here is my code: Approach 1
public class RealmManager {
private static final String TAG = "RealmManager";
private RealmAsyncTask transactionManager;
private static RealmManager mInstance = null;
public static RealmManager getInstance() {
if (mInstance == null)
mInstance = new RealmManager();
return mInstance;
}
private Realm mRealm;
protected RealmManager() {
mRealm = Realm.getDefaultInstance();
}
public void saveOrUpdateChatChannel(ChatChannel channel) {
mRealm = Realm.getDefaultInstance();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm bgRealm) {
bgRealm.copyToRealmOrUpdate(channel);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
Log.e(TAG,"Failed to update Channel");
}
});
}
public void deleteChatChannel(String channelID, OnRealmDatabaseListener mRealmListener) {
mRealm = Realm.getDefaultInstance();
mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
RealmResults<ChatChannel> result = realm.where(ChatChannel.class).equalTo("channelId", channelID).findAll();
result.deleteAllFromRealm();
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
if (mRealmListener != null)
mRealmListener.isDatabaseOperationSuccess(channelID, true);
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(#NonNull Throwable error) {
}
});
}
public void closeRealm() {
if (mRealm != null) {
mRealm.close();
}
if (transactionManager != null) {
transactionManager.cancel();
}
}
}
So in the Approach 1, I will be creating realm instances in my activities, services, intent services using RealmManager.getInstance() and then continue to do transactions. And in all my Activity onDestroy() methods i am closing the realm using RealmManager.closeRealm(). So my question is, if the RealmManager.closeRealm() which is called in Activity onDestroy(), will affect any transactions which are executing in Service?
Here is my code: Approach 2
public class RealmManager {
private static RealmManager mInstance = null;
public static RealmManager getInstance() {
if (mInstance == null)
mInstance = new RealmManager();
return mInstance;
}
private Realm mRealm;
protected RealmManager(){
mRealm = Realm.getDefaultInstance();
}
public void addClockModel(ClockRLM clockRLM,OnRealmDatabaseListener mRealmListener){
RealmAsyncTask transactionManager = mRealm.executeTransactionAsync(realm -> realm.copyToRealm(clockRLM), new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d("Clocke ", "Inserted TimeStamp " + clockRLM.getTimeStamp());
if (mRealmListener != null)
mRealmListener.isDatabaseOperationSuccess(clockRLM,true);
if (transactionManager != null)
transactionManager.cancel();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
if (transactionManager != null)
transactionManager.cancel();
}
});
}
}
So in the Approach 2, I will be creating realm instances in my activities, services, intent services using RealmManager.getInstance() and then continue to do transactions. I am not sure where to close realm if i use Approach 2. What if i do not close it anywhere and only when the app closes, the RealmManager gets destroyed and realm instance will be destroyed. OR i need to close the realm instance in the application level (I am not sure whether we can close instance in the application level).
Which one is better among Approach 1 and Approach 2. Or is it better to open a realm instance in onCreate() method of an Activity using relam = Realm.getDefaultInstance() and close it in onDestroy() using realm.close().
Realm is hard to use under a "singleton manager" because Realm.getDefaultInstance() might seem like you're getting something that's a "singleton", but it's really not. Instances of Realm are thread-local and reference-counted, each call to getInstance() increments a ref count while close() decrements it.
I've said a few times that open() would have been a better name, but I came to this conclusion far too late :)
First, the reason why your Singleton approach is not good is because:
Calls to the methods can only be done from the thread that first calls RealmManager.getInstance(), which is expected to the UI thread (but not guaranteed)
You hard-code that you want to execute each 1 operation in 1 async transaction, so you can't use this thing on a background thread too much
In order to make a realm manager that can be invoked on any threads, and UI thread uses async transaction while background thread uses sync transaction, you'd need to use a method like this.
And also, you'd need to track the open Realm instance for that given thread, so that you can access it wherever you want, without incrementing the ref count.
public class RealmManager {
private final ThreadLocal<Realm> localRealm = new ThreadLocal<>();
public Realm openLocalInstance() {
Realm realm = Realm.getDefaultInstance();
if(localRealm.get() == null) {
localRealm.set(realm);
}
return realm;
}
public Realm getLocalInstance() {
Realm realm = localRealm.get();
if(realm == null) {
throw new IllegalStateException("No open Realms were found on this thread.");
}
return realm;
}
public void closeLocalInstance() {
Realm realm = localRealm.get();
if(realm == null) {
throw new IllegalStateException(
"Cannot close a Realm that is not open.");
}
realm.close();
if(Realm.getLocalInstanceCount(Realm.getDefaultConfiguration()) <= 0) {
localRealm.set(null);
}
}
With a class like this, you could do:
try {
realmManager.openLocalInstance();
// Realm is open and can be accessed on this thread with `realmManager.getLocalInstance()`
// also all your methods in the RealmManager can use `getLocalInstance()`
} finally {
realmManager.closeLocalInstance();
}
I also created a library a while ago that wraps Realm in such a way that it eliminates the need for manual reference counting, but it never really caught on. Check out the source if curious.

How to keep Realm up-to-date across multiple background services in Android?

I'm trying to write data on one service and accessing it from another service.
The data I'm getting from another service is not updated, it's old copy.
Restarting the app sometimes gets updated data.
The services are both normal Service extended, so UI thread only.
And I'm not keeping any realm instances open anywhere in the app.
How do I ensure it's always new and updated one?
Writing -
data is detached using realm.copyFromRealm(...)
try (Realm realm = Realm.getDefaultInstance()) {
if (realm != null) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
data.setValue("New value ...");
realm.insertOrUpdate(data);
}
});
}
}
Reading -
Data data = null;
try (Realm realm = Realm.getDefaultInstance()) {
if (realm != null) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(#NonNull Realm realm) {
data = realm.copyFromRealm(realm.where(Data.class).equalTo("Id", id).findFirst());
}
});
}
}
Data -
public class Data {
...
private String Value;
public String getValue() { return Value; }
public void setValue(String v) { Value = v; }
...
}
Edit -
I ended up merging both services into one, which works for now. But I'll look forward if anyone can provide some tips or has a similar problem.
How do I ensure it's always new and updated one?
Don't use copyFromRealm(). and use RealmResults field variable + RealmChangeListener
RealmResults<T> results;
RealmChangeListener<RealmResults<T>> listener = new RealmChangeListener<...>() {
#Override public void onChange(RealmResults<T> results) {
...
}
}
public void something() {
results = realm.where(...).findAll*();
results.addChangeListener(

Realm transaction using from different thread

I have writeToRealm method that I use very often. And I need to use it from different threads and cut boilerplate code. What is the better way for this task?
private boolean writeToRealm(String user, String id) {
SubscriberObject subscriber = new SubscriberObject();
if(mRealm.where(SubscriberObject.class)
.equalTo(SubscriberObject.ID,id).findAll().isEmpty()
&&mRealm.where(SubscriberObject.class)
.equalTo(SubscriberObject.USERNAME,user).findAll().isEmpty()) {
subscriber.setId(id);
subscriber.setUsername(user);
mRealm.beginTransaction();
mRealm.insert(subscriber);
mRealm.commitTransaction();
return true;
}
return false;
}
I am planning to use construction below (or something like this) but I can't create a correct construction:
public static Boolean writeToRealm(final String user,final String id){
Realm mRealm;
return Flowable.using(
mRealm = Realm.getDefaultInstance(),
new Function<Realm, Boolean>() {
#Override
public Boolean apply(#NonNull Realm realm) throws Exception {
SubscriberObject subscriber = new SubscriberObject();
if(realm.where(SubscriberObject.class)
.equalTo(SubscriberObject.ID,id).findAll().isEmpty()
&&realm.where(SubscriberObject.class)
.equalTo(SubscriberObject.USERNAME,user).findAll().isEmpty()) {
subscriber.setId(id);
subscriber.setUsername(user);
realm.beginTransaction();
realm.insert(subscriber);
realm.commitTransaction();
return true;
}
return false;
}
},
mRealm.close()).subscribeOn(Schedulers.io());
}
Or may be I need to create a thread class with looper for this task?
How to better integrate this method and similar methods into a clean architecture?
I think you're just looking for
private boolean writeToRealm(String user, String id) {
try(Realm realm = Realm.getDefaultInstance()) {
if(realm.where(SubscriberObject.class).equalTo(SubscriberObject.ID,id).count() <= 0L
&& realm.where(SubscriberObject.class).equalTo(SubscriberObject.USERNAME,user).count() <= 0L) {
final SubscriberObject subscriber = new SubscriberObject();
subscriber.setId(id);
subscriber.setUsername(user);
realm.executeTransaction(r -> r.insert(subscriber));
return true;
}
}
return false;
}
I think this could be way an easier solution:
public static Boolean writeToRealm(String user, String id) {
Realm realm = Realm.getDefaultInstance();
SubscriberObject subscriber = new SubscriberObject();
if (realm.where(SubscriberObject.class).equalTo("ID", id).or().equalTo("USERNAME", user).findAll().isEmpty()){
subscriber.setId(id);
subscriber.setUsername(user);
realm.beginTransaction();
realm.insert(subscriber);
realm.commitTransaction();
realm.close();
return true;
}
realm.close();
return false;
}
If you need some explainations, just tell me and I will implement it :)
PS: if I missunderstood your question, let me know!
The asynchronous transaction support works the same way as the current executeTransaction, but instead of opening a Realm on the same thread, it will give you a background Realm opened on a different thread. You can also register a callback if you wish to be notified when the transaction completes or fails.
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Dog dog = realm.where(Dog.class).equalTo("age", 1).findFirst();
dog.setName("Fido");
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d("REALM", "All done updating.");
Log.d("BG", t.getName());
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// transaction is automatically rolled-back, do any cleanup here
}
});
read more
First of all you can't have boolean return type if you want to do your transaction asynchronously. You will have to use either Interface pass result back to caller or you have to opt for some other means like RxJava.
just to give you example.
The RxJava way(As this would be simplest way):
public static Flowable<Boolean> writeToRealm(final String user,final String id) {
return Flowable.fromCallable(
new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
Realm realm = Realm.getDefaultInstance();
if(realm.where(SubscriberObject.class)
.equalTo(SubscriberObject.ID,id).findAll().isEmpty()
&&realm.where(SubscriberObject.class)
.equalTo(SubscriberObject.USERNAME,user).findAll().isEmpty()) {
SubscriberObject subscriber = new SubscriberObject();
subscriber.setId(id);
subscriber.setUsername(user);
realm.beginTransaction();
realm.insert(subscriber);
realm.commitTransaction();
mRealm.close();
return true;
}
mRealm.close();
return false;
}
});
}
You subscribe the returned Flowable on desired thread/schedular to perform transaction on that particular thread.

How to maintain a single Realm instance throughout the app lifecycle and also close it?

How can I maintain a single Realm Instance throughout the complete lifecycle of the App and also close it.
I can achieve maintaining the instance using a singleton class, but then how do I close it when the app is closed?
Also, is it safe to not close Realm Instance once opened?
I tend to use a singleton RealmManager for the UI thread, and for background threads I open/close the Realm using a try-with-sources block.
So for UI thread:
public class RealmManager {
private static final String TAG = "RealmManager";
static Realm realm;
static RealmConfiguration realmConfiguration;
public static void initializeRealmConfig(Context appContext) {
if(realmConfiguration == null) {
Log.d(TAG, "Initializing Realm configuration.");
setRealmConfiguration(new RealmConfiguration.Builder(appContext).initialData(new RealmInitialData())
.deleteRealmIfMigrationNeeded()
.inMemory()
.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()) {
Log.w(TAG, "Unexpected open Realm found.");
realm.close();
}
}
Log.d(TAG, "Incrementing Activity Count [0]: opening Realm.");
realm = Realm.getDefaultInstance();
}
activityCount++;
Log.d(TAG, "Increment: Count [" + activityCount + "]");
}
public static void decrementCount() {
activityCount--;
Log.d(TAG, "Decrement: Count [" + activityCount + "]");
if(activityCount <= 0) {
Log.d(TAG, "Decrementing Activity Count: closing Realm.");
activityCount = 0;
realm.close();
if(Realm.compactRealm(realmConfiguration)) {
Log.d(TAG, "Realm compacted successfully.");
}
realm = null;
}
}
}
And for background thread:
try(Realm realm = Realm.getDefaultInstance()) {
// ...
}
Why don't you create a wrapping class for your realm instance (may be a singleton) and then add a few methods to it, so that instead of closing realm each time you can just call your own method and close the used instance as soon as you're finished? Something like this.
public class WrappingRealm {
public static WrappingRealm getInstance() {
//create your singleton here. Be aware of synchronization issues
}
private Realm getRealm() {
return Realm.getDefaultInstance();
}
public void save(RealmModel obj) {
Realm currentRealm = getRealm();
currentRealm.executeTransaction {
//Do your stuff
}
currentRealm.close();
}
}

Calling realm from AsyncTask

Is there any way to call Realm queries from AsyncTask?
I have so many queries that are doing join, So i want to call them from a separate One AsyncTask to avoid the load on UI Thread. For now i am using DefaultInstance of Realm everywhere. I get this error
Realm objects can only be accessed on the thread they where created
P.S I know Realm has its own Async for every query, but as i just mentioned i have alot of separate calls that are further doing joins and for loops.
EDIT
here's my code for an Async
#Override
protected Object doInBackground(Object[] params) {
//Step 1: Find All quote_taxes
Realm realm = Realm.getDefaultInstance();
listTaxData = new ArrayList<TaxData>();
try {
RealmResults<quote_taxes> listQuoteTaxes = quote_taxes.get_from_quotes(realm, quote.getId());
if (listQuoteTaxes != null && listQuoteTaxes.size() > 0) {
for (quote_taxes quoteTax : listQuoteTaxes) {
TaxData taxData = new TaxData();
taxData.setTaxName(quoteTax.getTaxName());
taxData.setAccountNumber("" + quoteTax.getAccountNumber());
taxData.setTaxRate("" + quoteTax.getTaxRate() + "%");
double total = quote_taxes.total(realm, quoteTax);
showLog("Total = " + total);
}
}
}catch (Exception ex)
{
}finally {
realm.close();
}
return null;
}
You just have to do what the docs say:
For AsyncTask this is a good pattern:
protected Void doInBackground(Void... params) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
// ... Use the Realm instance ...
} finally {
if (realm != null) {
realm.close();
}
}
return null;
}
More importantly, you can use try-with-resources:
protected Void doInBackground(Void... params) {
try(Realm realm = Realm.getDefaultInstance()) {
// ... Use the Realm instance ...
}
return null;
}
If you are using Thread or Runnable for short-lived tasks, the follow pattern is recommended:
// Run a non-Looper thread with a Realm instance.
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
// ... Use the Realm instance ...
} finally {
if (realm != null) {
realm.close();
}
}
}
});
thread.start();
And use a RealmChangeListener on the UI thread to be notified of successful transactions in background threads.
EDIT: Oh, you want to perform asynchronous queries.
I have so many queries that are doing join, So i want to call them from a separate One AsyncTask to avoid the load on UI Thread.
...while I truly doubt you have any "join"s considering Realm is not a relational database and the concept of joins doesn't exist in Realm; if you want asynchronous queries, you shouldn't overcomplicate your design with nonsense like AsyncTask. Just use the asynchronous query methods.
RealmResults<Something> results;
RealmChangeListener realmChangeListener = new RealmChangeListener() {
#Override
public void onChange(Object element) {
if(results != null && results.isValid() && results.isLoaded()) {
updateUI(results);
}
}
};
//...
results = realm.where(Something.class)./*...*/.findAllAsync(); // <-- async query
results.addChangeListener(realmChangeListener);
Realm has its Async load default functionality :-
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Use the Realm instance
}
});
Above execution done on background thread, and it's gives callback when db changes like.
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
// success callback
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// error callback
}
});
I believe that you create Realm objects in doInBackground and then process results in onPostExecute? To avoid this you can use IntentService instead of AsyncTask. If you still want to use AsyncTask, you can process query results in doInBackgroundas well and then return needed data (Lists, POJOs etc.) to onPostExecute.

Categories

Resources