-
public final class ReplicatorWorker extends CoroutineWorker
Implementation Notes:
Android does not support daemon processes. Although it is less likely to happen on modern phones with lots of memory, Android will still kill off a running application if it needs the memory space to run a new app. Under these circumstance, CouchbaseLite's continuous replication makes sense only while an application is in the foreground. Once an application is put in the background, it will, eventually, get killed and replicators will be stopped with prejudice. They will not be restarted until a user manually restarts the app and the replication.
In addition to this issue, continuous replication is incredibly wasteful of battery. It will force a mobile device to keep its radio on: the second most expensive thing a device can do, battery-wise.
Android provides a facility for managing long running processes: the
WorkManager
. Jobs scheduled with theWorkManager
are persistent. They are also batched across applications in order to optimize radio use. This package integrates replication into theWorkManage
. It works like this: Client code schedules replication work using normalWorkManager
code, like this:val factory = MyReplicatorFactory(). WorkManager.getInstance(context).enqueue( factory.periodicWorkRequestBuilder(15L, TimeUnit.MINUTES) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1L, TimeUnit.HOURS) .setConstraints( Constraints.Builder() .setRequiresBatteryNotLow(true) .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresStorageNotLow(true) .build() ) .build())
The only novelty here is that the client code obtains an instance of a
WorkRequest.Builder
using one of the factory methodsperiodicWorkRequestBuilder()
oroneShotWorkRequestBuilder()
defined on a client-created subclass ofWorkManagerReplicatorFactory
,MyReplicatorFactory
in the code above. These methods the are analogs of the standardPeriodicWorkRequestBuilder()
andOneShotWorkRequestBuilder()
extension functions and return the appropriateWorkRequest.Builder
that can be configured, built and scheduled, as any other WorkManager request.Remember, now, that the work manager may start an entirely new instance of the client application, every time it runs a new replication! None of the state from any previous instance of the application -- fields, objects, any of it -- may be available. The
ReplicatorWorker
, on the other hand, needs a properly constructedReplicatorConfiguration
in order to create and run a new replicator. This is why the client code creates a subclass ofWorkManagerReplicatorFactory
. When the factory method from the subclass ofWorkManagerReplicatorFactory
(periodicWorkRequestBuilder()
oroneShotWorkRequestBuilder()
) supplied theWorkRequest.Builder
, it used the value of the factory defined propertytag
, to store the FQN of the factory class, in the work request. TheReplicatorWorker
recovers this class name and creates a new instance using reflection. The instance is responsible for creating a properly constructedWorkManagerReplicatorConfiguration
from scratch and supplying it to theReplicatorWorker
.A
WorkManagerReplicatorConfiguration
is very similar to aReplicatorConfiguration
, except that it hides properties that should be configured in theWorkManager
request:continuous
,heartbeat
,maxAttempts
, andmaxAttemptWaitTime
.An application can observe the status of a work manager replication by subscribing to its
LiveData
. Assuming that replications processes are 1-1 with the factory defined tag (an assumption pervasive in this implementation) the client code can obtain aLiveData
object that providesReplicatorStatus
updates for the replicator like this:val replId = MyReplicatorFactory().tag val liveData = WorkManager.getInstance(context) .getWorkInfosByTagLiveData(replId) .map { if (it.isEmpty()) { null } else { it[0].progress.toReplicatorStatus(replId) } } return liveData
The application terminates a repeating WorkManager replication, normally, by cancelling all of the jobs for the identifying tag:
WorkManager.getInstance(context).cancelAllWorkByTag(factory.tag)
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description public class
ReplicatorWorker.Companion
-
Field Summary
Fields Modifier and Type Field Description private final CoroutineDispatcher
coroutineContext
-
Constructor Summary
Constructors Constructor Description ReplicatorWorker(Context appContext, WorkerParameters params)
-
Method Summary
Modifier and Type Method Description CoroutineDispatcher
getCoroutineContext()
ListenableWorker.Result
doWork()
-
Methods inherited from class androidx.work.CoroutineWorker
getApplicationContext, getBackgroundExecutor, getId, getInputData, getNetwork, getRunAttemptCount, getTags, getTaskExecutor, getTriggeredContentAuthorities, getTriggeredContentUris, getWorkerFactory, isStopped, isUsed, setForegroundAsync, setProgressAsync, setUsed, stop
-
Methods inherited from class com.couchbase.lite.internal.ReplicatorWorker
getForegroundInfo, getForegroundInfoAsync, onStopped, setForeground, setProgress, startWork
-
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
-
-
Constructor Detail
-
ReplicatorWorker
ReplicatorWorker(Context appContext, WorkerParameters params)
-
-
Method Detail
-
getCoroutineContext
CoroutineDispatcher getCoroutineContext()
-
doWork
ListenableWorker.Result doWork()
-
-
-
-