user6447397 user6447397 - 1 month ago 9
Android Question

RealmChangeListener does not get called when Realm gets updated in NotificationListenerService

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>

Answer

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), 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();
        msg.what = HandlerControllerConstants.LOCAL_COMMIT;
        realm.handlerController.handleMessage(msg);
    }
}

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.


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.