DBFlow icon indicating copy to clipboard operation
DBFlow copied to clipboard

addModelChangeListener is triggered multiple times

Open iAmMONK opened this issue 7 years ago • 8 comments

ISSUE_TEMPLATE

DBFlow Version: 4.2.4

Bug or Feature Request: addModelChangeListener is triggered multiple times

Description: I have 10 tables registered for content changes. When I do a normal item.save(), the addModelChangeListener is triggered 5 - 20 times.

iAmMONK avatar Nov 07 '18 09:11 iAmMONK

do you mean the method addModelChangeListener?

agrosner avatar Nov 07 '18 12:11 agrosner

observer = new FlowContentObserver(BuildConfig.APPLICATION_ID + ".authority");
observer.setNotifyAllUris(false);
observer.addModelChangeListener((table, action, primaryKeyValues) -> {
            /// This part is executed multiple times
});

I need to get the table name where the row was modified. Sometimes by doing :

FlowManager.getTableName(table).replaceAll("`", "");

It's throwing Exception :

11-07 18:18:13.202 8803-11209/com.axxessio.axxpos W/Binder: Caught a RuntimeException from the binder stub implementation.
    java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Class.getName()' on a null object reference
        at com.raizlabs.android.dbflow.config.FlowManager.getDatabaseForTable(FlowManager.java:141)
        at com.raizlabs.android.dbflow.config.FlowManager.getModelAdapterOrNull(FlowManager.java:469)
        at com.raizlabs.android.dbflow.config.FlowManager.getTableName(FlowManager.java:79)
        at com.axxessio.axxpos.server.SyncController.lambda$observeSyncChangeLogTable$4(SyncController.java:131)
        at com.axxessio.axxpos.server.-$$Lambda$SyncController$-XKgCVbUs8gjqBEic0pbirsM7l8.onModelStateChanged(lambda)
        at com.raizlabs.android.dbflow.runtime.FlowContentObserver.onChange(FlowContentObserver.java:285)
        at com.raizlabs.android.dbflow.runtime.FlowContentObserver.onChange(FlowContentObserver.java:254)
        at android.database.ContentObserver.onChange(ContentObserver.java:145)
        at android.database.ContentObserver.dispatchChange(ContentObserver.java:196)
        at android.database.ContentObserver.access$000(ContentObserver.java:27)
        at android.database.ContentObserver$Transport.onChange(ContentObserver.java:231)
        at android.database.IContentObserver$Stub.onTransact(IContentObserver.java:62)
        at android.os.Binder.execTransact(Binder.java:453)

Why is it even possible to get the table as null ?

iAmMONK avatar Nov 07 '18 13:11 iAmMONK

in 5.0.0-alpha1 with Kotlin we have almost full null safety involved and that will not happen. As for now I'd recommend checking for null and discard any event that returns a null table. As for why that is happening - it may be receiving events from other tables since its likely using the same Context object when you register the observer. My advice would be to switch to a DirectModelObserver if you don't need the ContentResolver functionality for now and that should work the way you want. In 5.0.0+ I am planning on overhauling the notification system to work as expected and efficiently as possible.

agrosner avatar Nov 07 '18 14:11 agrosner

Basically what I need to achieve is whenever we add or modify a row, I need to get the table name and rowId to push it to the server. What would you recommend for that ? Also I couldn't find DirectModelObserver in 4.2.4 . There is only DirectModelNotifier which has private acces.

UPD: Okey I've found how to use DirectModelNotifier but it requires to set Listener for every table. Or is there other way to make as with FlowContentObserver ?

iAmMONK avatar Nov 07 '18 14:11 iAmMONK

DirectModelNotifier is a little different in implementation. use DirectModelNotifier.get() to utilize the instance and register / deregister for ModelChangedListener. It'll give you the direct model object and what changed. You can get it's id and class (which is the table) from the callback. And in your DB you'll need to override the default ContentResolverNotifier to switch to it. https://dbflow.gitbook.io/dbflow/v/develop/usage2/usage/observability#direct-changes Even though the doc is for 5.0.0+, it's very similar on how to utilize it.

agrosner avatar Nov 07 '18 14:11 agrosner

Future plans would try to involve utilizing db triggers and checking for changes to a new, custom table which keeps track of these changes in the SQLite layer, thus working with any kind of DB modification or query pretty efficiently.

agrosner avatar Nov 07 '18 14:11 agrosner

@agrosner I've just tried to use DirectModelNotifier but anyway it's executed twice(still better than previously). But I didn't try to put multiple listeners(for each table).

I've just put a log into the ModelChangeListener and that's the result:

11-07 20:50:13.380 12163-12163/com E/myLogs: Unit
11-07 20:50:13.381 12163-12163/com E/myLogs: Unit
11-07 20:50:22.771 12163-12163/com E/myLogs: Unit
11-07 20:50:22.771 12163-12163/com E/myLogs: Unit

That's on Inserting new Items. like

Unit u = new Unit()
u.setName();
u.setDate();
u.setFoo();
u.save();

Maybe I am doing something wrong ?

iAmMONK avatar Nov 07 '18 16:11 iAmMONK

DbFlow: v 4.2.4

I have tried both addOnTableChangedListener and addModelChangeListener and they are trigged for all the Models not just the one we have registered for. ( check this )

In case of the non registered tables the Class<?> table` is null.

My temporary workaround is to add a null check for Class<?> table

observer.addModelChangeListener((table, action, parameters) -> {
            if(table != null){
            doSomthing(table, action,parameters);}
        });

ubaierbhat avatar Jan 24 '19 10:01 ubaierbhat