From 795cd6e25fd1a930f4b0bda74f8344db64dad777 Mon Sep 17 00:00:00 2001 From: Nabil Hachicha Date: Wed, 17 Feb 2016 20:03:34 +0000 Subject: [PATCH] Refactor notify listeners --- .../java/io/realm/RealmAsyncQueryTests.java | 9 --- .../java/io/realm/RxJavaTests.java | 3 +- .../io/realm/TypeBasedNotificationsTests.java | 15 ++-- .../src/main/java/io/realm/BaseRealm.java | 6 +- .../main/java/io/realm/HandlerController.java | 70 ++++++++----------- 5 files changed, 44 insertions(+), 59 deletions(-) diff --git a/realm/realm-library/src/androidTest/java/io/realm/RealmAsyncQueryTests.java b/realm/realm-library/src/androidTest/java/io/realm/RealmAsyncQueryTests.java index 1487b9459b..0c9df91782 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/RealmAsyncQueryTests.java +++ b/realm/realm-library/src/androidTest/java/io/realm/RealmAsyncQueryTests.java @@ -16,13 +16,10 @@ package io.realm; -import android.content.Context; import android.os.Handler; import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -57,18 +54,12 @@ @RunWith(AndroidJUnit4.class) public class RealmAsyncQueryTests { - private Context context; @Rule public final RunInLooperThread looperThread = new RunInLooperThread(); @Rule public final TestRealmConfigurationFactory configFactory = new TestRealmConfigurationFactory(); - @Before - public void setUp() throws Exception { - context = InstrumentationRegistry.getInstrumentation().getContext(); - } - // **************************** // **** Async transaction *** // **************************** diff --git a/realm/realm-library/src/androidTest/java/io/realm/RxJavaTests.java b/realm/realm-library/src/androidTest/java/io/realm/RxJavaTests.java index af8bb22d94..70f44415f1 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/RxJavaTests.java +++ b/realm/realm-library/src/androidTest/java/io/realm/RxJavaTests.java @@ -54,12 +54,11 @@ public class RxJavaTests { @Rule public final TestRealmConfigurationFactory configFactory = new TestRealmConfigurationFactory(); - private RealmConfiguration realmConfig; private Realm realm; @Before public void setUp() throws Exception { - realmConfig = configFactory.createConfiguration(); + RealmConfiguration realmConfig = configFactory.createConfiguration(); realm = Realm.getInstance(realmConfig); } diff --git a/realm/realm-library/src/androidTest/java/io/realm/TypeBasedNotificationsTests.java b/realm/realm-library/src/androidTest/java/io/realm/TypeBasedNotificationsTests.java index f10ae9294d..0bc84aa21f 100644 --- a/realm/realm-library/src/androidTest/java/io/realm/TypeBasedNotificationsTests.java +++ b/realm/realm-library/src/androidTest/java/io/realm/TypeBasedNotificationsTests.java @@ -855,10 +855,15 @@ public void callback_with_relevant_commit_realmresults_async() { @Override public void onChange() { // Step 4: Respond to relevant change - typebasedCommitInvocations.incrementAndGet(); - assertEquals("Size wong", 1, dogs.size()); - assertEquals("Akamaru", dogs.get(0).getName()); - assertEquals(17, dogs.get(0).getAge()); + int commits = typebasedCommitInvocations.incrementAndGet(); + switch (commits) { + case 2: + assertEquals(17, dogs.get(0).getAge()); + case 1: + assertEquals(1, dogs.size()); + assertEquals("Akamaru", dogs.get(0).getName()); + + } } }); @@ -885,7 +890,7 @@ public void onChange() { realm.handler.post(new Runnable() { @Override public void run() { - assertEquals("Typebased wrong", 1, typebasedCommitInvocations.get()); + assertEquals(2, typebasedCommitInvocations.get()); looperThread.testComplete(); } }); diff --git a/realm/realm-library/src/main/java/io/realm/BaseRealm.java b/realm/realm-library/src/main/java/io/realm/BaseRealm.java index 088ab7c7c8..07fd3054fb 100644 --- a/realm/realm-library/src/main/java/io/realm/BaseRealm.java +++ b/realm/realm-library/src/main/java/io/realm/BaseRealm.java @@ -201,7 +201,8 @@ public void removeAllChangeListeners() { handlerController.removeAllChangeListeners(); } - // WARNING: If this method is used after calling a any async method, the old handler will still be used. + // WARNING: If this method is used after calling any async method, the old handler will still be used. + // package private, for test purpose only void setHandler(Handler handler) { // remove the old one handlers.remove(this.handler); @@ -270,8 +271,7 @@ public void refresh() { } sharedGroupManager.advanceRead(); if (handlerController != null) { - handlerController.notifyGlobalListeners(); - handlerController.notifyTypeBasedListeners(); + handlerController.notifyAllListeners(); // if we have empty async RealmObject then rerun if (handlerController.threadContainsAsyncEmptyRealmObject()) { handlerController.updateAsyncEmptyRealmObject(); diff --git a/realm/realm-library/src/main/java/io/realm/HandlerController.java b/realm/realm-library/src/main/java/io/realm/HandlerController.java index 408b43b5f1..0f1d4875d0 100644 --- a/realm/realm-library/src/main/java/io/realm/HandlerController.java +++ b/realm/realm-library/src/main/java/io/realm/HandlerController.java @@ -112,12 +112,10 @@ public boolean handleMessage(Message message) { } case COMPLETED_UPDATE_ASYNC_QUERIES: { // this is called once the background thread completed the update of the async queries - notifyAllListeners(); QueryUpdateTask.Result result = (QueryUpdateTask.Result) message.obj; completedAsyncQueriesUpdate(result); break; } - case REALM_ASYNC_BACKGROUND_EXCEPTION: { // Don't fail silently in the background in case of Core exception throw (Error) message.obj; @@ -178,14 +176,14 @@ void removeAllChangeListeners() { void notifyGlobalListeners() { // notify strong reference listener Iterator iteratorStrongListeners = changeListeners.iterator(); - while (iteratorStrongListeners.hasNext()) { + while (iteratorStrongListeners.hasNext() && !realm.isClosed()) { // every callback could close the realm RealmChangeListener listener = iteratorStrongListeners.next(); listener.onChange(); } // notify weak reference listener (internals) Iterator> iteratorWeakListeners = weakChangeListeners.iterator(); List> toRemoveList = null; - while (iteratorWeakListeners.hasNext()) { + while (iteratorWeakListeners.hasNext() && !realm.isClosed()) { WeakReference weakRef = iteratorWeakListeners.next(); RealmChangeListener listener = weakRef.get(); if (listener == null) { @@ -202,12 +200,6 @@ void notifyGlobalListeners() { } } - void notifyTypeBasedListeners() { - notifyAsyncRealmResultsCallbacks(); - notifySyncRealmResultsCallbacks(); - notifyRealmObjectCallbacks(); - } - void updateAsyncEmptyRealmObject() { Iterator, RealmQuery>> iterator = emptyAsyncRealmObject.entrySet().iterator(); while (iterator.hasNext()) { @@ -228,6 +220,17 @@ void updateAsyncEmptyRealmObject() { } } + void notifyAllListeners() { + notifyGlobalListeners(); + notifyTypeBasedListeners(); + } + + private void notifyTypeBasedListeners() { + notifyAsyncRealmResultsCallbacks(); + notifySyncRealmResultsCallbacks(); + notifyRealmObjectCallbacks(); + } + private void notifyAsyncRealmResultsCallbacks() { notifyRealmResultsCallbacks(asyncRealmResults.keySet().iterator()); } @@ -250,7 +253,8 @@ private void notifyRealmResultsCallbacks(Iterator realmResults : resultsToBeNotified) { + for (Iterator> it = resultsToBeNotified.iterator(); it.hasNext() && !realm.isClosed(); ) { + RealmResults realmResults = it.next(); realmResults.notifyChangeListeners(); } } @@ -274,7 +278,8 @@ private void notifyRealmObjectCallbacks() { } } - for (RealmObject realmObject : objectsToBeNotified) { + for (Iterator it = objectsToBeNotified.iterator(); it.hasNext() && !realm.isClosed(); ) { + RealmObject realmObject = it.next(); realmObject.notifyChangeListeners(); } } @@ -330,29 +335,16 @@ private void realmChanged() { updateAsyncQueries(); } else { - RealmLog.d("REALM_CHANGED realm:"+ HandlerController.this + " no async queries, advance_read"); + RealmLog.d("REALM_CHANGED realm:" + HandlerController.this + " no async queries, advance_read"); realm.sharedGroupManager.advanceRead(); notifyAllListeners(); - } - } - - private void notifyAllListeners() { - notifyGlobalListeners(); - // notify RealmResults & RealmObject callbacks (type based notifications) - if (!realm.isClosed()) { - // Realm could be closed in the above listener. - notifySyncRealmResultsCallbacks(); - } - if (!realm.isClosed()) { - notifyRealmObjectCallbacks(); - } - - // empty async RealmObject shouldn't block the realm to advance - // they're empty so no risk on running into a corrupt state - // where the pointer (Row) is using one version of a Realm, whereas the - // current Realm is advancing to a newer version (they're empty anyway) - if (!realm.isClosed() && threadContainsAsyncEmptyRealmObject()) { - updateAsyncEmptyRealmObject(); + // empty async RealmObject shouldn't block the realm to advance + // they're empty so no risk on running into a corrupt state + // where the pointer (Row) is using one version of a Realm, whereas the + // current Realm is advancing to a newer version (they're empty anyway) + if (!realm.isClosed() && threadContainsAsyncEmptyRealmObject()) { + updateAsyncEmptyRealmObject(); + } } } @@ -474,13 +466,11 @@ private void completedAsyncQueriesUpdate(QueryUpdateTask.Result result) { query.notifyChangeListeners(); } - // notify listeners only when we advanced - if (compare != 0) { - notifyGlobalListeners(); - // notify RealmResults & RealmObject callbacks (type based notifications) - notifySyncRealmResultsCallbacks(); - notifyRealmObjectCallbacks(); - } + // We need to notify the rest of listeners, since the original REALM_CHANGE + // was delayed/swallowed in order to be able to update async queries + notifyGlobalListeners(); + notifySyncRealmResultsCallbacks(); + notifyRealmObjectCallbacks(); updateAsyncQueriesTask = null; }