Android realm.io: Row/Object is no longer valid - android

This is my delete function and it does find the workday1 object:
public static void delete(Context context, Workday workday) {
Realm realm = getRealm(context);
realm.beginTransaction();
Workday workday1 = realm.where(Workday.class)
.equalTo("date", workday.getDate())
.equalTo("hours", workday.getHours())
.equalTo("minutes", workday.getMinutes())
.findFirst();
workday1.removeFromRealm();
realm.commitTransaction();
}
When it executes the removeFromRealm method it crashes:
java.lang.IllegalStateException: Illegal State: Row/Object is no longer valid to operate on. Was it deleted?
How can I fix this? Any help would be greatly appreciated.
UPDATE (I can print the content returned by the following method):
Workday workday1 = realm.where(Workday.class)
.equalTo("date", workday.getDate())
.equalTo("hours", workday.getHours())
.equalTo("minutes", workday.getMinutes())
.findFirst();
System.out.println("--------------------------------");
System.out.println(workday1.getHours());

You are trying to remove an object you have not committed to the Realm yet.
In this particular case, if for some reason you don't want to commit the object anymore, you can simply cancel the transaction.

In my case, the problem was that the same Object I was deleting was in an Adapter. After I made the Adapter extend RealmBaseAdapter the problem stopped.

Related

How to Clear the RealmResults<> of a particular Query while Filtering through the Realm in Android?

I am applying filters on realm using RealmResults<>.
I begin to do like this -
RealmResults<data> filteredRealmResults;
List<data> tranfilteredlist;
private OrderedRealmCollectionChangeListener<RealmResults<data>> filteredTransChangeListener =
new OrderedRealmCollectionChangeListener<RealmResults<data>>() {
#Override
public void onChange(RealmResults<data> results, OrderedCollectionChangeSet changeSet) {
Log.d("realm", "filteredRealmResults.size():" + filteredRealmResults.size());
tranfilteredlist = results;
initFilterAdapter();
}
};
Now I want to delete the filteredRealmResults. I did like this -
void deleteFilteredRealmResults() {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Delete all matches
filteredRealmResults.deleteAllFromRealm();
}
});
}
After doing this my data in the realm got deleted. So I just try to delete the tranfilteredlist but it throws an exception that it does not support .clear();
I want to clear if from the memory whatever is holder the query data. Correct me if I am wrong or doesn't understand or just worrying too much.
I read This class holds all the matches of a RealmQuery for a given Realm. The objects are not copied from the Realm to the RealmResults list, but are just referenced from the RealmResult instead. This saves memory and increases speed.
I want to clear if from the memory whatever is holder the query data.
Correct me if I am wrong or doesn't understand or just worrying too
much.
Once you invoke filteredRealmResults.deleteAllFromRealm, it will clear the internal resultant elements object(which holds the elements) and as you know, resultant objects are reference so data will be deleted from realm database too. Hence, there is no need to call clear on the RealmResults object.
You can verify this by calling filteredRealmResults.size() after deletion, it will return 0.
I just try to delete the tranfilteredlist but it throws an exception
that it does not support .clear();
It is the expected behaviour as clear has been deprecated so don't use it.
Why deprecated?
deleteAllFromRealm automatically clears the list so no need to call it again explicitly.
Calling clear on RealmResults object will result in deletion of data from database, can cause unexpected behaviour if the user is not aware so API is being modified to avoid unexpected behaviours.

Realm instance has already been closed - with RxJava2

I used Realm in conjunction with RxJava it this way:
public Flowable<List<EventEntity>> getAll() {
try (final Realm realm = Realm.getInstance(mRealmConfiguration)) {
RealmQuery<RealmEvent> query = realm.where(RealmEvent.class);
Flowable<RealmResults<RealmEvent>> result;
if (realm.isAutoRefresh()) {
result = query
.findAllAsync()
.asFlowable()
.filter(RealmResults::isLoaded);
} else {
result = Flowable.just(query.findAll());
}
return result
.unsubscribeOn(AndroidSchedulers.mainThread());
}
}
I use this chain on multiple places in app. For example:
return Observable.merge(
mEventRepository.getAll()
.toObservable(),
subjectNotificationChange
.flatMapMaybe(notification ->
mEventRepository.getAll()
.firstElement()
)
)
Problem is that I obtain exception: java.lang.IllegalStateException: This Realm instance has already been closed, making it unusable.
I looked at implementation method from of RealmObservableFactory and each call of subscribe method should create new instance of Realm. Entire situation looks as problem with references counting.
Do you know where is problem?
Java's try-with-resource closes the resource as soon as you leave the code block, but RxJava being lazy and all, only begins working when you actually subscribe, which happens after your code exits the getAll() function.
Edit: since you build a special Realm instance each time, passing configuration to it, the instance is not shared and therefore definitively closed each time.
Instead, initialize your Realm earlier using Realm.setDefaultConfiguration(config). Then, use Realm.getDefaultInstance() in your function so you access the default shared instance instead of creating a new one each time.
Edit2: the easiest solution is to keep a reference to the Realm instance:
class MyRepository {
private final Realm realm;
public MyRepository(Realm realm) {
this.realm = realm;
}
public Flowable<List<EventEntity>> getAll() {
RealmQuery<RealmEvent> query = realm.where(RealmEvent.class);
// ...
}
}
Realm realm = Realm.getDefaultInstance();
MyRepository repository = MyRepository(realm);
repository.getAll()
// ...
I find solution. It is bug in official example. When you call mentioned chain than must exist other open Realm instance for same thread. In other cases RealmResult is invalidated. Can be used solution mentioned by ESala.

Realm DB how to get query output object as unmanaged?

I am trying to query my Realm DB such that the output will give an unmanaged object and for that, I changed my RealmList type of object to List.
Now the thing is in addchangeListener I am getting my output object(stories) value as managed. But the type of stories is List. So why my stories object is becoming managed where it should act as an unmanaged object.
List<story> stories = realm.where(story.class).findAllAsync();
stories.addChangeListener(new RealmChangeListener<RealmResults<story>>() {
#Override
public void onChange(RealmResults<story> storydata) {
if (storydata.size() != 0) {
madapter = new StoriesAdapter(stories, getBaseContext(), MR);
mrecyclerview.setNestedScrollingEnabled(false);
mrecyclerview.setLayoutManager(new LinearLayoutManager(getBaseContext()));
mrecyclerview.setAdapter(madapter);
}
}
});
StoriesAdapter
class StoriesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<story> storyList;
StoriesAdapter(List<story> storyList) {
this.storyList = storyList;
}
}
I am saying my List is managed because when i am trying to write below code I am getting Cannot modify managed objects outside of a write transaction.
madapter.storyList.get(3).setTitle("Wonderland"); // where storyList is List which i am pointing to `stories`.
List<story> stories = realm.where(story.class).findAllAsync();
Because specifying the type List<story> just means you'll see the returned list as a List<story>, but technically it's still a RealmResults<story>.
stories.addChangeListener(new RealmChangeListener<RealmResults<story>>() {
This line underneath shouldn't even compile.
Stories should be stored in a field.
private RealmResults<story> stories;
public void ...() {
stories = ...
stories.addChangeListener(...
Anyways, so you are working with RealmResults, which means that in
class StoriesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<story> storyList;
This storyList you provided is a RealmResults<story>, so calling storyList.get(...) will return managed RealmObjects.
Managed RealmObjects are "temporarily immutable", meaning they can only be modified in a transaction. It is also generally not recommended to run write transactions on the UI thread.
The simplest way would be to use realm-android-adapters.
class StoriesAdapter extends RealmRecyclerViewAdapter<story, RecyclerView.ViewHolder> {
StoriesAdapter(OrderedRealmCollection<story> stories) {
super(stories, true, true);
}
}
And when you want to modify an object, you do
story item = getData().get(3);
final String id = item.getId();
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
story changedItem = realm.where(story.class).equalTo("id", id).findFirst();
changedItem.setTitle("Wonderland");
}
});
And then Realm will handle automatically updating the RealmResults, the story object, and the RecyclerView.
EDIT: If you intend to use unmanaged objects, then you could use realm.copyFromRealm(results), except that does the read on the UI thread.
You could create a background looper thread and obtain the results from there, but managing that could be tricky. Luckily for you, there's a library I made called Monarchy which lets you do exactly that.
See the relevant sample code for how you'd use it.
The stories is implicitly Managed, the reason is that RealmResults extends the list interface abstractly. Thats why the casting is possible, underneath the same mechanisms for a RealmResults still takes precedence. Also, you should only pass RealmResults instance to an Adapter directly, if you register a RealmChangeListener on it, which will call adapter.notifyDataSetChanged(). Otherwise, writes will update the RealmResults content, and your adapter will be desynchronized.
Realm is not like SQLite or Core Data. If you’re using Realm, take advantage of live objects. Don’t implement any refreshing logic or requerying. Always allow the current class to own its own instance of a realm query.
This fact is true,Realm objects and any child objects are NOT thread-safe. They’re confined to a single thread to ensure that atomic rights are maintained. There is an internal list where every single thread has its own unique Realm instance. If you want to pass objects between a thread–for example, if you create a dog object on the main thread, pass it to the background thread, and then try and access a property–it will trigger an exception straight away.
Also you are using asynchronous query, which puts it on a worker thread.

Realm with RxAndroid not picking up latest data changes

I am using Realm with RxAndroid. i am having this strange issue where realm is not picking up the latest modification done on DB.
There are 2 methods that i am using.
Observable<Integer> save(Bitmap bitmap).
Observable<Integer> getImageList(Context applicationContext).
Like this
Activity 1
getImageList(applicationContext)
button click -> Activity 2
save(bitmap)
finish()
getImageList(applicationContext)
This method "save" basically adds a newly created model into RealmList.
private Observable<Integer> save(Bitmap bitmap) {
return Observable.create((Observable.OnSubscribe<Integer>) subscriber -> {
--------------------------------------
-----Various file creation stuff------
--------------------------------------
UserImagesModel model = realm
.where(UserImagesModel.class)
.findFirst();
//ImageModel class extends RealmObject
ImageModel imageModel = new ImageModel();
realm.beginTransaction();
//realm object must be Edited inside transaction
model.getResponse().add(0, imageModel);
realm.commitTransaction();
realm.close();
subscriber.onNext(1);
subscriber.onCompleted();
}
}
Ans this method fetches saved list.
public Observable<Integer> getImageList(Context applicationContext) {
return Observable.create((Observable.OnSubscribe<Integer>) subscriber -> {
AppUtils.logD("User image observable instance " + this);
UserImagesModel model;
Realm realm = Realm.getInstance(applicationContext);
model = realm.where(UserImagesModel.class).findFirst();
^
This model doesn't replicate data added in save call
------------------------------------------------
----Various validation and service calls.-------
------------------------------------------------
subscriber.onCompleted();
realm.close();
});
}
}
As i mentioned in code, UserImageModel that i get from Realm doesn't replicate changes i made in save method.
the problem occurs when i call getImageList method second time. also when i print this.toString inside Observable.create it prints same object that was returned first time.
So i believe this issue seems to be with the way i am using RxAndroid. can anyone tell me what i am missing? and how can i resolve it?
UPDATE :
After few tests i realized that this.toString inside Observable.create is actually points to parent object as i have used lamda expression so that is not seems to be the issue and now i am back to square one ;(
Turns out, this is expected behavior of Realm. as i was subscribing those observables on IO threads which doesn't have Looper.
Op here has similar issue. answer explains the case.

Android realm.io variables not set?

I am having trouble using a query in realm.io. My code:
public static void delete(Context context, Workday workday) {
Realm realm = getRealm(context);
realm.beginTransaction();
RealmResults<Workday> workdays = realm.where(Workday.class)
.equalTo("date", workday.getDate())
.equalTo("hours", workday.getHours())
.equalTo("minutes", workday.getMinutes())
.findAll();
workdays.remove(0);
realm.commitTransaction();
}
Debug:
Why is the data visible in the JSON line but not in the fields itself? What am I doing wrong?
Thanks in advance!
UPDATE:
This is my delete function and it does find the workday1 object:
public static void delete(Context context, Workday workday) {
Realm realm = getRealm(context);
realm.beginTransaction();
Workday workday1 = realm.where(Workday.class)
.equalTo("date", workday.getDate())
.equalTo("hours", workday.getHours())
.equalTo("minutes", workday.getMinutes())
.findFirst();
workday1.removeFromRealm();
realm.commitTransaction();
}
When it executes the removeFromRealm method it crashes:
java.lang.IllegalStateException: Illegal State: Row/Object is no longer valid to operate on. Was it deleted?
How can I fix this?
You're doing nothing wrong! :)
Realm proxies your objects so that there won't be copies of your data all over the place. The getters and setters are overridden by the proxy classes and access your data directly in Realm. Of course this makes it harder to inspect the objects during debug, as you could notice, but that's why we've also overridden toString() to show something meaningful.

Categories

Resources