Couchbase Lite runs replications (syncs) using background threads. Starting and stopping replications doesn’t happen synchronously. This can lead to mistakes in detecting the state of a replication.
The Replication class has, depending on platform, either a running
property or a convenience routine like isRunning()
. This is a lightweight way of checking the status of a replication.
Using it can lead to unexpected results, though. For example, I recently wrote a utility that uses continuous replications. I have a toggle button to start and stop them. It’s tempting to use isRunning
to update the button state. Turns out this is a bad idea. Not surprising, given changes happen in the background, but easy to overlook.
Instead, the preferred approach uses a change listener. Here’s an example.
We have two classes. The database helper class wraps some standard operations for simplicity. I added an interface to employ a callback pattern. The client class has to implement it. The helper class digests the Couchbase Lite change notification before passing anything to the client. This gives a nice separation of concerns.
The code listings below are outlines. They only show essentials. Let’s look at the helper class first.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
public class DBHelper implements Replication.ChangeListener { private boolean replicationActive = false; private List stateListeners = new ArrayList<>(); ... public interface ReplicationStateListener { void onChange(boolean isActive); } public void startReplication(URL gateway, boolean continuous) { ... pushReplication.addChangeListener(this); pushReplication.start(); } public void stopReplication() { ... } public void addReplicationStateListener(ReplicationStateListener listener) { stateListeners.add(listener); } public void removeReplicationStateListener(ReplicationStateListener listener) { stateListeners.remove(listener); } // Replication.ChangeListener @Override public void changed(Replication.ChangeEvent changeEvent) { if (changeEvent.getError() != null) { Throwable lastError = changeEvent.getError(); // React to the error return; } if (changeEvent.getTransition() == null) return; ReplicationState dest = changeEvent.getTransition().getDestination(); replicationActive = ((dest == ReplicationState.STOPPING || dest == ReplicationState.STOPPED) ? false : true); stateListeners.forEach(listener -> listener.onChange(replicationActive)); } } |
We see that DBHelper implements the Replication.ChangeListener
interface. This is an interface defined by Couchbase Lite. In startReplication
we add this listener to the replication. It has one method to override, changed
. The ChangeEvent
passed in can have several different values. In the example, I check for errors first and notify the user if one occurs. Otherwise I check to see if this is a replication state transition event. The destination state can be one of INITIAL
, RUNNING
, IDLE
, OFFLINE
, STOPPING
, or STOPPED
.
In this case I just want to know if the replication is shutting down, so I simplify the return value. I allow clients to register more than one listener, so the last bit of code loops over all the callbacks and invokes them.
The client class is even simpler. I have the class implement the needed interface from the helper class. Just register the listener during instance construction, and have onChange
do whatever you need in the UI.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class Client implements DBHelper.ReplicationStateListener { private DBHelper service = DBHelper.getInstance(); ... public Client() { service.addReplicationStateListener(this); } ... // DBHelper.ReplicationStateListener @Override public void onChange(boolean isActive) { // Code to handle the change } } |
This code is drawn from a tool I built. Read about the tool itself in this post.You can find the source code on GitHub here.
Postscript
Check out more resources on our developer portal and follow us on Twitter @CouchbaseDev.
You can post questions on our forums. And we actively participate on Stack Overflow.
Hit me up on Twitter @HodGreeley
[…] comes from the DBService helper class. I wrote a bit about detecting the state of a replication here. For this app I just need to know whether a replication is running or not to keep the Sync button […]
[…] The other listener allows us to display a busy-wait spinner (indefinite progress bar) depending on the Replication state. You can read more about monitoring replication state here. […]