Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RealmChangeListener does not get called when Realm gets updated in NotificationListenerService

Tags:

android

realm

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>
like image 276
AsterixR Avatar asked Aug 08 '16 15:08

AsterixR


1 Answers

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();
like image 155
EpicPandaForce Avatar answered Oct 24 '22 08:10

EpicPandaForce