5

I'm doing Realm insertions on a extended NotificationListenerService, like this:

public class NLService extends NotificationListenerService {

    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {

        // building realmObject here

        mRealm = Realm.getDefaultInstance();
        RealmHelper.saveObj(myRealmObject, mRealm);
    //  mRealm.waitForChange(); / mRealm.refresh();
        mRealm.close();
    }
}


public class RealmHelper {

    public static RealmModel saveObj(RealmObject realmObject, Realm realm) {
        realm.beginTransaction();
        RealmObject newRealmObject = realm.copyToRealm(realmObject);
        realm.commitTransaction();
        return newRealmObject;
    }
}

Using Realm newer than v0.88.3, not a single RealmChangeListener (rcl) gets called if anything gets inserted in NLService.

I tried attaching rcl's directly to Realm, RealmResults and RealmObject, nothing works.

The App has for example simple rcl's for RealmResults<myRealmObject>.size() and several RecyclerAdapters and the rcl inside RealmRecyclerViewAdapter is never called.

Rerunning queries however works and the "missing data" shows up. Also if anything gets inserted on ui- or any other thread, rcl's get called and "missing data" shows up.

I stayed for months on Realm 0.88.3 because I can not bring it to work with any newer Realm version. With 0.88.3 mRealm.refresh(); was called in NLService, this is not available in newer versions and .waitForChange blocks endlessly.

Manifest.xml:

<service
    android:name=".service.NLService"
    android:label="@string/nl_service"
    android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
    <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService"/>
    </intent-filter>
</service>
AsterixR
  • 547
  • 7
  • 9
  • Where did you try to add `RealmChangeListener`? Do you by chance know if `NotificationListenerService` is running on a background thread (non-looper), on a background thread (with looper), or on the UI thread? – EpicPandaForce Aug 08 '16 at 22:44
  • Greatest question is, how is the `NotificationListenerService` declared in your AndroidManifest.xml? – EpicPandaForce Aug 08 '16 at 23:20
  • (note: Referenced in https://github.com/realm/realm-java/issues/3272 for later) – EpicPandaForce Aug 09 '16 at 05:41
  • I am not that familiar with NotificationListenerService, but if it is being called in a separate process that is why it doesn't work. – Christian Melchior Aug 09 '16 at 08:53
  • @EpicPandaForce see edit. All RealmChangeListeners are on the UI thread. – AsterixR Aug 09 '16 at 10:08
  • `NotificationListenerService` is started by the system, afaik the app has no control about it, but im not sure. – AsterixR Aug 09 '16 at 10:14
  • If `mRealm = Realm.getDefaultInstance();` created the first Realm instance in your service thread, you actually don't need to call `Refresh()` since it does open the latest version of Realm. – beeender Aug 09 '16 at 11:47

1 Answers1

19

I can see two solutions to this, either use a looper thread (HandlerThread) with setAutoRefresh(true) (and call setAutoRefresh(false) before Looper.quit()), or force a refresh for the Realm instance on the thread.


NOTE: This relies on package-internal methods. Beware.

In v 1.1.1 (and v1.2.0), - and any version before 3.0.0 - instead of the following line

//  mRealm.waitForChange(); / mRealm.refresh();

You could force the update on the local thread through the HandlerController associated with the Realm instance using package-internal stuff

package io.realm;

public class RealmRefresh {
    public static void refreshRealm(Realm realm) {
        Message message = Message.obtain();
        message.what = HandlerControllerConstants.LOCAL_COMMIT;
        realm.handlerController.handleMessage(message);
    }
}

And then call

    mRealm = Realm.getDefaultInstance();
    RealmHelper.saveObj(myRealmObject, mRealm);
    RealmRefresh.refreshRealm(mRealm);
    mRealm.close();

Please note the change log's breaking changes though, because 0.89.0 changed iteration behavior, and results are no longer live during an active transaction; but from 3.0.0 they are again.


However, I must also note that if your NotificationListenerService is running in a remote process, then the Realm instances won't be able to notify each other.



EDIT:

In Realm Java 3.0.0, the notification behavior was changed completely, and HandlerController no longer exists.

Instead, the following should work:

package io.realm;

public class RealmRefresh {
    public static void refreshRealm(Realm realm) {
        realm.sharedRealm.refresh();
    }
}

EDIT:

In Realm 3.2.+, this is all available with

realm.refresh();
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • 1
    Can confim, this solution works. However there appears to be a litte lag on the ui thread, i'll try to investigate if its related to this. Thanks for your help @EpicPandaForce! – AsterixR Aug 09 '16 at 10:41
  • Glad to hear! :) Although this is more a bandaid than a fix, so there's further discussion going on in the meantime to figure out what's up. – EpicPandaForce Aug 09 '16 at 12:14
  • 1
    And apparently `HandlerController` is still exactly where it was and this part works as it did before, so this still works under 2.0.2. – EpicPandaForce Oct 07 '16 at 10:53
  • 1
    I am confirming this works even with Realm 2.2.1, it is life saving.. i have to call it in the begin of AsyncTask.doInBackground() to have actual data (if i call it on end, i see the old data for first next calls after change). I wonder how is it possible that such big problem as not updated data in non looper thread in realm must be fixed by someone in SO and not by Realm developers by default api.. it is really big problem and this helps much. – luky Dec 13 '16 at 08:45
  • 1
    @luky technically I filed an issue 3 months ago [here](https://github.com/realm/realm-java/issues/3476) but it's `P2: Design Required` so there's that. I'll have to update this post when the results integration is complete (that's when this solution will no longer work), but that'll probably be next year. – EpicPandaForce Dec 13 '16 at 09:00
  • Yeah I figured Realm 3.0.0 and results integration would break this. I'll look into a replacement in the source when I get to a PC. – EpicPandaForce Mar 02 '17 at 08:51
  • @FireZenk please try the added solution and see if it works as a replacement – EpicPandaForce Mar 02 '17 at 09:58
  • `sharedRealm` does not exist. How could I make this work? – John Ernest Guadalupe Mar 06 '17 at 07:05
  • Yes it does it is right [there](https://github.com/realm/realm-java/blob/master/realm/realm-library/src/main/java/io/realm/BaseRealm.java#L72) , just do the package that the answer tells you (also that's in 3.0.0 and above) – EpicPandaForce Mar 06 '17 at 08:46
  • Btw, This still happens in 5.8.0. I just had to call .refresh() before querying data on a second thread. – Alon Minski Nov 16 '19 at 10:35