Search code examples
androidrealm

RealmObject updated on background thread is not getting update or notification


I have a model Member extends RealmObject. In my activity, I set onAddChangeListener on it as follows:

private RealmChangeListener<RealmResults<Member>> memberTracker = this::setupProfileUI;
private Member member;

private void setupProfileUI(RealmResults<MemberDetails> results) {
    member = results.first(null);
    updateProfileUI();
}

and in onCreate(), I do

    RealmResults<Member> memberResults = realm.where(Member.class).findAll();
    memberResults.addChangeListener(memberTracker);
    member = memberResults.first(null);

I update member object on a background thread based on selection of a spinner

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {

            Request.updateMember(new Request.Callback<Member>() {
                        @Override
                        public void onResponse(Response<Member> response) {
                            Log.d(TAG, "updateMember: " + member.getLocation().getCity());
                            Log.d(TAG, "updateMember: " + response.body().getLocation().getCity());
                        }

                        @Override
                        public void onFailure(Throwable t) {
                            Log.e(TAG, "onFailure: Member Update Failed");
                        }
                    });
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {
        }
    });

The second Log statement in onResponse callback prints Member's city from response from an API while first prints city value from current value of member. The later value remains unchanged and Activity receives no update Notification.

Member is updated in Request.updateMember() through a call to a helper function as follows

private static void updateMemberDetails(Response<Member> response) {
    if (response.isSuccessful()) {
        try(Realm realmInstance = Realm.getDefaultInstance()) {
            Member member = response.body();
            if (member != null) {
                realmInstance.executeTransaction(realm -> realm.insertOrUpdate(member));
            }
        }
    }
}

I checked the .realm file manually on successful async call that member in .realm file was indeed updated but the member object in Activity doesn't gets updated nor there is any update notification.


Solution

  • RealmResults<Member> memberResults = realm.where(Member.class).findAll();
    

    As per documentation of RealmResults.addChangeListener :

    Registering a change listener will not prevent the underlying RealmResults from being garbage collected. If the RealmResults is garbage collected, the change listener will stop being triggered. To avoid this, keep a strong reference for as long as appropriate e.g. in a class variable.

     public class MyActivity extends AppCompatActivity {
    
       private RealmResults<Person> results; // Strong reference to keep listeners alive
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         results = realm.where(Person.class).findAllAsync();
         results.addChangeListener(new RealmChangeListener<RealmResults<Person>>() {
             @Override
             public void onChange(RealmResults<Person> persons) {
                 // React to change
             }
         });
       }
     }
    

    So you should do exactly that.