Realm 4.1.
My fragment code:
public class FragmentSort extends Fragment {
private Realm realm;
private RealmResults<Offer> offersRealmResults;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.offers_sort_fragment, container, false);
init(rootView);
return rootView;
}
private void init(View rootView) {
ButterKnife.bind(this, rootView);
realm = Realm.getDefaultInstance();
offersRealmResults = StoreService.getOffers(realm,orderBy);
initLogic()
}
private void initLogic() {
offersRealmResults.addChangeListener(new RealmChangeListener<RealmResults<Offer>>() {
@Override
public void onChange(RealmResults<Offer> offers) {
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
recyclerView.setAdapter(null);
realm.removeAllChangeListeners();
realm.close();
}
}
Here StoreService's method:
public static RealmResults<Offer> getOffers(Realm realm, String sortByFieldName) {
return realm.where(Offer.class).findAllSorted(sortByFieldName, Sort.DESCENDING);
}
Here my adapter code:
public class OfferSortAdapter extends RealmRecyclerViewAdapter<Offer, OfferSortAdapter.OfferViewHolder> implements RecyclerOnItemClickListener {
public void onItemClicked(View view, int position) {
Realm realm = Realm.getDefaultInstance();
try {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Offer offer = realm.where(Offer.class).equalTo(Offer.ID, offerId).findFirst();
offer.setDownloaded(downloaded);
}
});
} finally {
realm.close();
}
}
}
After call method onItemClickedthe offer success changed in Realm. Changed field "downloaded" from from false to true.
But method onChange()in Fragment not call.
Why?
Is the Realm you got the results from kept open?
In fragment:
public View onCreateView(LayoutInflater inflater, ViewGroup container, @Nullable Bundle savedInstanceState) {
realm = Realm.getDefaultInstance();
}
...
In fragment I here destroy realm
@Override
public void onDestroy() {
super.onDestroy();
recyclerView.setAdapter(null);
realm.removeAllChangeListeners();
realm.close();
}
So I think it's open
What is the offersRealmResults that you are adding the change listener to?
realm.where[???].[???].find[???]();
On your code what you get is a simple NullPointerException. Please complete the code.
Ps: Don't open an transaction on the UI Thread.
I update my post
Could the Realm instance passed to the getOffers be closed before the transaction happen?
In my fragment I close Realm in onDestroy() So Realm is live
is onChange() never called or just some times not called?
My realm change on 2 cases:
List<Merchant> and save to Realm by: ```
public static void updateMerchants(final List
Realm realm = Realm.getDefaultInstance();
try {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
RealmList
merchantsRealmList.addAll(newMerchantsList);
realm.copyToRealmOrUpdate(merchantsRealmList);
}
});
} finally {
realm.close();
}
}
And after success finish `updateMerchants()` then method `onChange()` always call.
Nice.
Also I have button. When click I call this:
public static void update(final int offerId, final boolean downloaded) {
Realm realm = Realm.getDefaultInstance();
try {
realm.executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
Offer offer = realm.where(Offer.class).equalTo(Offer.ID, offerId).findFirst();
offer.setDownloaded(downloaded);
});
} finally {
realm.close();
}
}
```
As result field downloaded success updated in Realm, but method onChange() not call
Should initLogic() be called in the init()? from the source you posted, it doesn't seem so?
Also, try to call update(offId, downloaded) right after addChangeListener to test if the onChange() is called.
Should initLogic() be called in the init()? from the source you posted, it doesn't seem so?
Yes, I update my post
Also, try to call update(offId, downloaded) right after addChangeListener to test if the onChange() is called.
Here test code:
private void initLogic() {
offersRealmResults.addChangeListener(new RealmChangeListener<RealmResults<Offer>>() {
@Override
public void onChange(RealmResults<Offer> offers) {
}
});
// test
Offer offerBefore = OfferService.getOffer(5);
OfferService.update(5, true);
}
Result:
after success OfferService.update(5, true) method onChange success call.
@lmdic Are you able to share your project with us? to [email protected] if you want to share it privately. I feel the realm instance was closed or the offersRealmResults has been GCed. But from the code you shared I cannot tell what exactly is going wrong there.
If you cannot share the project, would you please call RealmLog.setLevel(LogLevel.All) at the beginning of the application and get a full logcat for us (from the app starts to a few seconds after the button clicked.) And please add some log to your key functions like initLogic and OfferService.update() as well so it will be easier to read the log.
What is offersRealmResults exactly?
@Zhuinden search for the offersRealmResults in the main post, it has been updated.
I sent email to [email protected] with topic "My project's logcat with RealmLog.setLevel(LogLevel.All) on Android 4.3".
@lmdic I got your trace.
according to the trace, it feels that the transaction was not executed in onClick().
realm.executeTransaction() will produce trace like:
4163 10-30 12:34:42.841 V/REALM_JNI(10139): --> Java_io_realm_internal_SharedRealm_nativeBeginTransaction xxxxxx
But I searched trace for com.myproject.android.customer.ui.adapter.OfferSortAdapter(xxx): onItemClicked in the log, it doesn't show that the onClick triggers the executeTransaction().
Is the code different from what you posted here?
@lmdic It seems the getOffers() is called in the onChange() callback. Would you please post the source code of public void onChange(RealmResults<Offer> offers) as well?
In my fragment:
private void initLogic() {
offersRealmResults.addChangeListener(new RealmChangeListener<RealmResults<Offer>>() {
@Override
public void onChange(RealmResults<Offer> offers) {
updateData();
}
});
}
private void updateData() {
offersRealmResults = MyService.getOffers(realm, categoryId, downloaded, orderBy);
offerSortAdapter.updateData(offersRealmResults);
}
In service class:
public static RealmResults<Offer> getOffers(Realm realm, Integer categoryId, Boolean downloaded, String sortByFieldName) {
return getOffers(realm, downloaded, sortByFieldName);
}
public static RealmResults<Offer> getOffers(Realm realm, Boolean downloaded, String sortByFieldName) {
return realm.where(Offer.class).findAllSorted(sortByFieldName, Sort.DESCENDING);
}
OK, I think calling updateData() in the onChange() is the problem.
The updateData() will re-set the offersRealmResults which causes the old value of offersRealmResults gets GCed. But you listener was added to the old offersRealmResults object. When it gets GCed, the listener won't be triggered anymore.
RealmResults will be automatically updated, i don't think you need to re-query it.
Is I need to remove call updateData(); from method onChange() ?
But after Realm was changed I need to update RealmRecyclerViewAdapter by
offerSortAdapter.updateData(offersRealmResults);
If I don't do this, the UI will not refresh. Or maybe Realm do this automatically?
Is I'm right?
RealmRecyclerViewAdapter should handle refreshing UI automatically.
See examples https://github.com/realm/realm-android-adapters/blob/master/example/src/main/java/io/realm/examples/adapters/ui/recyclerview/MyRecyclerViewAdapter.java
Nah he seems to set it as field variable. I do not think this should cause a problem.
I think there is just a different categoryId to be shown or something, and fine-grained listeners aren't called because the given category does not change.
RealmRecyclerViewAdapter should handle refreshing UI automatically.
Do you mean this:
https://github.com/realm/realm-android-adapters/blob/master/adapters/src/main/java/io/realm/RealmRecyclerViewAdapter.java
RealmBaseRecyclerAdapter - This adapter will automatically handle any updates to its data and call {@code notifyDataSetChanged()},
* {@code notifyItemInserted()}, {@code notifyItemRemoved()} or {@code notifyItemRangeChanged(} as appropriate
?
Yes
So I remove call method updateData()from onChange()
```
private void initLogic() {
offersRealmResults.addChangeListener(new RealmChangeListener
@Override
public void onChange(RealmResults
// not need manually update UI because adapter automatically handle any updates to its data
}
});
}
Here my adapter:
public class OfferSortAdapter extends RealmRecyclerViewAdapter
@Override
public void onBindViewHolder(OfferSortAdapter.OfferViewHolder holder, final int position) {
// my code here
}
}
```
So now it's work fine.
And now method OfferSortAdapter.onBindViewHolder()call every time when Realm was changed.
This happen because OfferSortAdapteris automatically handle any updates to its data.
So I'm NOT need manually call offerSortAdapter.updateData(offersRealmResults)to refresh GUI.
Is I'm right?
Yeah although you can also try it and see if it works
Yes now it's work. Thanks.