Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nh/refresh send realmchanged #2319

Closed
wants to merge 9 commits into from
46 changes: 29 additions & 17 deletions realm/realm-library/src/androidTest/java/io/realm/RealmTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -2053,26 +2053,33 @@ public void run() {
}
}

// FIXME HandlerThread
// This test assure that calling refresh will not trigger local listeners
// after the Looper receives REALM_CHANGE message
@Test
public void processLocalListenersAfterRefresh() throws InterruptedException {
public void processRefreshLocalListenersAfterLooperQueueStart() throws Throwable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this be simplified immensely using @RunTestInLooperThread?

@Test
@RunTestInLooperThread
public void test() {
// Setup listeners and stuff. 
realm.refresh()
// Verify that listeners haven't been called yet
}

// Used to validate the result
final AtomicBoolean listenerWasCalled = new AtomicBoolean(false);
final AtomicBoolean typeListenerWasCalled = new AtomicBoolean(false);

// Used by the background thread to wait for the main thread to do the write operation
final CountDownLatch bgThreadLatch = new CountDownLatch(1);
final CountDownLatch bgClosedLatch = new CountDownLatch(1);
final CountDownLatch bgClosedLatch = new CountDownLatch(2);
final CountDownLatch bgThreadReadyLatch = new CountDownLatch(1);
final CountDownLatch signalClosedRealm = new CountDownLatch(1);

Thread backgroundThread = new Thread() {
final Looper[] looper = new Looper[1];
final Throwable[] throwable = new Throwable[1];

ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new Runnable() {
@Override
public void run() {
// this will allow to register a listener.
// we don't start looping to prevent the callback to be invoked via
// the handler mechanism, the purpose of this test is to make sure refresh calls
// the listeners.
Looper.prepare();
looper[0] = Looper.myLooper();

Realm bgRealm = Realm.getInstance(realmConfig);
RealmResults<Dog> dogs = bgRealm.where(Dog.class).findAll();
Expand All @@ -2081,34 +2088,34 @@ public void run() {
@Override
public void onChange() {
listenerWasCalled.set(true);
bgClosedLatch.countDown();
}
});
dogs.addChangeListener(new RealmChangeListener() {
@Override
public void onChange() {
typeListenerWasCalled.set(true);
bgClosedLatch.countDown();
}
});

bgThreadReadyLatch.countDown();
bgThreadLatch.await(); // Wait for the main thread to do a write operation
bgRealm.refresh(); // This should call the listener
assertTrue(listenerWasCalled.get());
assertTrue(typeListenerWasCalled.get());
bgRealm.close();
bgRealm = null;
// DON'T count down in the final block! The test will fail silently!!!
bgClosedLatch.countDown();
} catch (InterruptedException e) {
fail(e.getMessage());
assertFalse(listenerWasCalled.get());
assertFalse(typeListenerWasCalled.get());

Looper.loop();

} catch (Throwable e) {
throwable[0] = e;

} finally {
if (bgRealm != null) {
bgRealm.close();
}
bgRealm.close();
signalClosedRealm.countDown();
}
}
};
backgroundThread.start();
});

// Wait until bgThread finishes adding listener to the RealmResults. Otherwise same TableView version won't
// trigger the listener.
Expand All @@ -2118,6 +2125,11 @@ public void onChange() {
realm.commitTransaction();
bgThreadLatch.countDown();
bgClosedLatch.await();

TestHelper.exitOrThrow(executorService, bgClosedLatch, signalClosedRealm, looper, throwable);

assertTrue(listenerWasCalled.get());
assertTrue(typeListenerWasCalled.get());
}

private void populateForDistinct(Realm realm, long numberOfBlocks, long numberOfObjects, boolean withNull) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
import io.realm.entities.Dog;
import io.realm.entities.Owner;
import io.realm.entities.PrimaryKeyAsLong;
import io.realm.proxy.HandlerProxy;
import io.realm.rule.RunInLooperThread;
import io.realm.rule.RunTestInLooperThread;
import io.realm.rule.TestRealmConfigurationFactory;
Expand Down Expand Up @@ -836,7 +835,6 @@ public void run() {
looperThread.testComplete();
}
});

}
}
});
Expand Down Expand Up @@ -899,7 +897,6 @@ public void run() {
looperThread.testComplete();
}
});

}
}
});
Expand Down Expand Up @@ -1402,25 +1399,12 @@ public void onChange() {

// ****************************************************************************************** //
// UC 5.
// Callback should be notified if we call refresh (even without getting the REALM_CHANGE yet)
// Callback should be notified if we call refresh
// ***************************************************************************************** //
@Test
@RunTestInLooperThread
public void refresh_should_notify_callbacks_realmobject_sync() {
final Realm realm = looperThread.realm;
// Swallow all REALM_CHANGED events to test the behaviour of refresh
final Handler handler = new HandlerProxy(realm.handlerController) {
@Override
public boolean onInterceptInMessage(int what) {
switch (what) {
case HandlerController.REALM_CHANGED: {
return true;
}
}
return false;
}
};
realm.setHandler(handler);

realm.beginTransaction();
realm.createObject(Dog.class);
Expand Down Expand Up @@ -1462,28 +1446,17 @@ public void run() {
@RunTestInLooperThread
public void refresh_should_notify_callbacks_realmobject_async() {
final Realm realm = looperThread.realm;
// Swallow all REALM_CHANGED events to test the behaviour of refresh
final Handler handler = new HandlerProxy(realm.handlerController) {
@Override
public boolean onInterceptInMessage(int what) {
switch (what) {
case HandlerController.REALM_CHANGED: {
return true;
}
}
return false;
}
};
realm.setHandler(handler);

final Dog dog = realm.where(Dog.class).findFirstAsync();
assertTrue(dog.load());

dog.addChangeListener(new RealmChangeListener() {
@Override
public void onChange() {
assertEquals("Akamaru", dog.getName());
looperThread.testComplete();
if (dog.isValid()) {
assertEquals("Akamaru", dog.getName());
looperThread.testComplete();
}
}
});

Expand Down Expand Up @@ -1513,20 +1486,6 @@ public void run() {
@RunTestInLooperThread
public void refresh_should_notify_callbacks_realmresults_sync() {
final Realm realm = looperThread.realm;
// Swallow all REALM_CHANGED events to test the behaviour of refresh
final Handler handler = new HandlerProxy(realm.handlerController) {
@Override
public boolean onInterceptInMessage(int what) {
switch (what) {
case HandlerController.REALM_CHANGED: {
return true;
}
}
return false;
}
};
realm.setHandler(handler);

final RealmResults<Dog> dogs = realm.where(Dog.class).findAll();

dogs.addChangeListener(new RealmChangeListener() {
Expand Down Expand Up @@ -1563,19 +1522,6 @@ public void run() {
@RunTestInLooperThread
public void refresh_should_notify_callbacks_realmresults_async() {
final Realm realm = looperThread.realm;
// Swallow all REALM_CHANGED events to test the behaviour of refresh
final Handler handler = new HandlerProxy(realm.handlerController) {
@Override
public boolean onInterceptInMessage(int what) {
switch (what) {
case HandlerController.REALM_CHANGED: {
return true;
}
}
return false;
}
};
realm.setHandler(handler);

final RealmResults<Dog> dogs = realm.where(Dog.class).findAllAsync();
assertTrue(dogs.load());
Expand Down Expand Up @@ -1618,19 +1564,6 @@ public void refresh_should_notify_callbacks_mixed() {
final CountDownLatch listenerWasCalledOnRealmResults = new CountDownLatch(1);

final Realm realm = looperThread.realm;
// Swallow all REALM_CHANGED events to test the behaviour of an explicit refresh
final Handler handler = new HandlerProxy(realm.handlerController) {
@Override
public boolean onInterceptInMessage(int what) {
switch (what) {
case HandlerController.REALM_CHANGED: {
return true;
}
}
return false;
}
};
realm.setHandler(handler);

Dog dog = realm.where(Dog.class).findFirstAsync();
RealmResults<Dog> dogs = realm.where(Dog.class).findAllAsync();
Expand Down Expand Up @@ -1678,6 +1611,8 @@ public void run() {
}

realm.refresh();
assertEquals(1, listenerWasCalledOnRealmObject.getCount());
assertEquals(1, listenerWasCalledOnRealmResults.getCount());
}

// Test modifying realmObjects in RealmObject's change listener
Expand Down
13 changes: 6 additions & 7 deletions realm/realm-library/src/main/java/io/realm/BaseRealm.java
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,12 @@ public void refresh() {
if (isInTransaction()) {
throw new IllegalStateException(BaseRealm.CANNOT_REFRESH_INSIDE_OF_TRANSACTION_MESSAGE);
}
sharedGroupManager.advanceRead();
if (handlerController != null) {
handlerController.notifyAllListeners();
// if we have empty async RealmObject then rerun
if (handlerController.threadContainsAsyncEmptyRealmObject()) {
handlerController.updateAsyncEmptyRealmObject();
}
if (handlerController == null) {
// non Looper Thread, just advance the Realm
// registering listeners is not allowed, hence nothing to notify
sharedGroupManager.advanceRead();
} else {
handlerController.realm.handler.sendEmptyMessage(HandlerController.REALM_CHANGED);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,10 +419,8 @@ private void completedAsyncQueriesUpdate(QueryUpdateTask.Result result) {
SharedGroup.VersionID callerVersionID = realm.sharedGroupManager.getVersion();
int compare = callerVersionID.compareTo(result.versionID);
if (compare > 0) {
RealmLog.d("COMPLETED_UPDATE_ASYNC_QUERIES realm:" + HandlerController.this + " caller is more advanced, rerun updates");
// The caller is more advance than the updated queries ==>
// need to refresh them again (if there is still async queries)
realm.handler.sendEmptyMessage(REALM_CHANGED);
// if the caller thread is advanced i.e it already sent a REALM_CHANGE that will update the queries
RealmLog.d("COMPLETED_UPDATE_ASYNC_QUERIES realm:" + HandlerController.this + " caller is more advanced, Looper will updates queries");

} else {
// We're behind or on the same version as the worker thread
Expand Down