Reliably stop sync when app is in background on Android


#1

Hi,

I’m on Android 6.x, device is Nexus 5, CBL version is 1.2 and ForestDb is in use.

Push- and pull-replication is set to continuous = true. After leaving my app by pressing the home button and making sure that everything was synced. [I’m the only one using the app and I only receive documents which match my channel. So I made very much sure that there is no more documents to receive or to upload.] Then I stop the sync_gateway service on my server and I’ll see this warning and repeated two line error message in my Android logs. The two line error messages repeated for more than 30 minutes. It does not seem to stop.

02-29 10:11:44.684 28281-29056 W/ChangeTracker: com.couchbase.lite.replicator.ChangeTracker@184550ae: Exception in change tracker
                                                java.io.EOFException
                                                    at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:169)
                                                    at java.util.zip.GZIPInputStream.read(GZIPInputStream.java:140)
                                                    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.loadMore(UTF8StreamJsonParser.java:180)
                                                    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._skipWSOrEnd2(UTF8StreamJsonParser.java:2777)
                                                    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._skipWSOrEnd(UTF8StreamJsonParser.java:2772)
                                                    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:652)
                                                    at com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer$Vanilla.deserialize(UntypedObjectDeserializer.java:443)
                                                    at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringMap(MapDeserializer.java:473)
                                                    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:330)
                                                    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:25)
                                                    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3560)
                                                    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2646)
                                                    at com.couchbase.lite.replicator.ChangeTracker.runLoop(ChangeTracker.java:358)
                                                    at com.couchbase.lite.replicator.ChangeTracker.run(ChangeTracker.java:228)
                                                    at java.lang.Thread.run(Thread.java:818)
02-29 10:11:44.754 28281-29056 W/ChangeTracker: com.couchbase.lite.replicator.ChangeTracker@184550ae: Longpoll connection closed (by proxy?) after 0 sec
02-29 10:11:44.756 28281-28388 E/Sync: Change tracker stopped during continuous replication
02-29 10:11:54.931 28281-10374 E/ChangeTracker: com.couchbase.lite.replicator.ChangeTracker@b7eb31f: Change tracker got error 502
02-29 10:11:54.931 28281-28388 E/Sync: Change tracker stopped during continuous replication
02-29 10:12:05.187 28281-10536 E/ChangeTracker: com.couchbase.lite.replicator.ChangeTracker@94690ca: Change tracker got error 502
02-29 10:12:05.187 28281-28388 E/Sync: Change tracker stopped during continuous replication

Hence I assume that the sync replicator in CBL is waiting in the background for new data to receive and keeps a connection with the SG alive. Regardless if the SG is up and running or shut down. I’m concerned about my app’s battery usage.

TLDR
Is there a reliably way to stop and start the continuous sync on Android?

Thanks, Ben


#2

Continuous replications keep running until stopped. Did you try calling Replication.stop()?


#3

I do not call Replication.stop(). The replication is started in the application class. Is this a good place to do so? I would also call stop in the application class. What is the recommended practice to call start/stop and where? Activity onResume/onPause?

Application class does not have a lifecycle. That is specific to Android. I’d be following this answer to determine when the app is no longer in the foreground and hence the replication can be stopped, and then call Replication.stop()


#4

@benjamin_glatzeder,
It depends on your application. Couchbase Lite for Android does not pause automatically when Activity enter background. If your app is single Activity app, Activity’s state change is good place to start/stop replicator.


#5

Hi,

would you mind to elaborate on the “it depends” part. We are using the contionous replication in order to receive the changes no matter in which Activity we are. So making the replicaton stop and start in the corresponding activity lifecycle methods doesn’t seem sufficient. Furthermore it generates additional traffic (~15kb per stop and start) and on top of that throws errors like the following one in some cases:

java.lang.IllegalStateException: No valid leaving transitions are permitted from state 'STOPPED' for trigger 'START'. Consider ignoring the trigger.

respectively this one:

W/Sync: replicationInternal in unexpected state: STOPPING, ignoring start()

As previously mentioned the Application class doesn’t have any Lifecycle callbacks out of the box so there’s no way to tell when the application is dismissed by the user or inactive for x minutes.
So in that case what’s your recommendation when to start/stop the replication?

Thanks


#6

I’m not sure what you’re asking, exactly. You can stop and restart replication whenever you want. If you want to run it in the background, do so; if you want to stop it because of battery use, then stop it. It’s up to you.

Another option is to trade battery life for some latency by polling. You can stop the continuous pull, but use a timer to periodically run a non-continuous pull. This will just ping the server (two REST requests), download any changes, and then stop.

There’s no penalty for leaving continuous push replications running, btw. Push does not leave any TCP connections open.

(I can’t comment on the error messages you reported, because I don’t work on the Java version of CBL. The best thing to do is probably to file a bug report for each one, on Github.)


#7

Hi, thanks for your answer.

Basically I just wanted to know when to stop the replication since it seems to be running forever even if the application isn’t active anymore - I guess the Replication is running as a Service? So ultimately this leads to a battery drain - we see about 10% per hour so I don’t know if I can agree with you on the “there’s no penalty for leaving continuous push replications running” part.

Nevertheless we’re now using a workaround to track the number of active activities as described here. Currently this approach seems to work the best and prevents the issue we’ve described.


#8

Hm. You said “We are using the contionous replication in order to receive the changes no matter in which Activity we are”. But you’re complaining that “it seems to be running forever even if the application isn’t active anymore”.

…Oh, OK. After reading the SO link it makes sense. I’m not an Android developer so I didn’t understand the distinction between an Activity and the application itself. Yeah, if you want the replication to stop when the app is in the background, just use the technique in the article to detect that. (This would probably be a good option to add to our Android API.)

I don’t know if I can agree with you on the “there’s no penalty for leaving continuous push replications running” part.

You’re probably leaving the pull replication running. That will leave a socket open and consume power. The push replication, however, will not, as I said.


#9

Use this to know your app state…http://steveliles.github.io/is_my_android_app_currently_foreground_or_background.html

Background and pull: Also listen to replication state(RS) listener…when you are in background and once RS moves active to idle…stop the replicator… Thats what i do in my app and works well…
@jens ens @hideki make sense?

Thanks
Nithin


#10

Hi,

Firstly, I’d recommend that Manager and Database instances would be held by Application. Activity is destroyed and re-crated when the screen changes orientation, and Android destroys Activities under high memory usage. Activity is not best place.

About the continuous replicator start and stop, this depends on application’s requirements.
If the application needs to replicate when the app is in background, start() and stop() from Application could be choice. With this approach, the app keeps to consume battery. End-user could be unhappy about the battery life. Also the community developer experienced that creating sticky service with this approach caused unexpected problems.
If the application requires the replication when the app is in foreground, the app needs to start() and stop() replicator by detecting app state with using Application.ActivityLifecycleCallbacks.

Thanks,
Hideki