Skip to content

Commit d5d6592

Browse files
authored
SyncWorker: use Provider for lazy injection (#925)
* SyncWorker: use Provider for lazy injection * Use Provider to lazyily inject SyncAdapter
1 parent ac48e65 commit d5d6592

File tree

5 files changed

+61
-69
lines changed

5 files changed

+61
-69
lines changed

app/src/androidTest/kotlin/at/bitfire/davdroid/sync/worker/TestSyncWorker.kt

+1-10
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,15 @@ package at.bitfire.davdroid.sync.worker
66

77
import android.content.Context
88
import androidx.work.WorkerParameters
9-
import at.bitfire.davdroid.settings.AccountSettings
10-
import at.bitfire.davdroid.sync.SyncConditions
11-
import at.bitfire.davdroid.ui.NotificationRegistry
129
import dagger.hilt.android.qualifiers.ApplicationContext
1310
import io.mockk.mockk
1411
import kotlinx.coroutines.Dispatchers
1512
import javax.inject.Inject
1613

1714
class TestSyncWorker @Inject constructor(
18-
@ApplicationContext context: Context,
19-
accountSettingsFactory: AccountSettings.Factory,
20-
notificationRegistry: NotificationRegistry,
21-
syncConditionsFactory: SyncConditions.Factory
15+
@ApplicationContext context: Context
2216
): BaseSyncWorker(
2317
context,
2418
workerParams = mockk<WorkerParameters>(),
25-
accountSettingsFactory,
26-
notificationRegistry,
27-
syncConditionsFactory,
2819
syncDispatcher = Dispatchers.Main
2920
)

app/src/main/kotlin/at/bitfire/davdroid/sync/SyncAdapterServices.kt

+15-11
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,24 @@ import at.bitfire.davdroid.R
2121
import at.bitfire.davdroid.log.Logger
2222
import at.bitfire.davdroid.settings.AccountSettings
2323
import at.bitfire.davdroid.sync.worker.OneTimeSyncWorker
24-
import dagger.hilt.EntryPoint
25-
import dagger.hilt.InstallIn
26-
import dagger.hilt.android.EntryPointAccessors
24+
import dagger.hilt.android.AndroidEntryPoint
2725
import dagger.hilt.android.qualifiers.ApplicationContext
28-
import dagger.hilt.components.SingletonComponent
2926
import kotlinx.coroutines.CancellationException
3027
import kotlinx.coroutines.CompletableDeferred
3128
import kotlinx.coroutines.cancel
3229
import kotlinx.coroutines.runBlocking
3330
import kotlinx.coroutines.withTimeout
3431
import java.util.logging.Level
3532
import javax.inject.Inject
33+
import javax.inject.Provider
3634

3735
abstract class SyncAdapterService: Service() {
3836

39-
@EntryPoint
40-
@InstallIn(SingletonComponent::class)
41-
interface SyncAdapterServiceEntryPoint {
42-
fun syncAdapter(): SyncAdapter
43-
}
37+
@Inject
38+
lateinit var syncAdapter: Provider<SyncAdapter>
4439

4540
override fun onBind(intent: Intent?): IBinder {
46-
val entryPoint = EntryPointAccessors.fromApplication<SyncAdapterServiceEntryPoint>(this)
47-
return entryPoint.syncAdapter().syncAdapterBinder
41+
return syncAdapter.get().syncAdapterBinder
4842
}
4943

5044
/**
@@ -150,8 +144,18 @@ abstract class SyncAdapterService: Service() {
150144
}
151145

152146
// exported sync adapter services; we need a separate class for each authority
147+
148+
@AndroidEntryPoint
153149
class CalendarsSyncAdapterService: SyncAdapterService()
150+
151+
@AndroidEntryPoint
154152
class ContactsSyncAdapterService: SyncAdapterService()
153+
154+
@AndroidEntryPoint
155155
class JtxSyncAdapterService: SyncAdapterService()
156+
157+
@AndroidEntryPoint
156158
class OpenTasksSyncAdapterService: SyncAdapterService()
159+
160+
@AndroidEntryPoint
157161
class TasksOrgSyncAdapterService: SyncAdapterService()

app/src/main/kotlin/at/bitfire/davdroid/sync/worker/BaseSyncWorker.kt

+43-37
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import androidx.work.WorkQuery
1919
import androidx.work.WorkerParameters
2020
import at.bitfire.davdroid.InvalidAccountException
2121
import at.bitfire.davdroid.R
22-
import at.bitfire.davdroid.log.Logger
2322
import at.bitfire.davdroid.settings.AccountSettings
2423
import at.bitfire.davdroid.sync.AddressBookSyncer
2524
import at.bitfire.davdroid.sync.CalendarSyncer
@@ -30,24 +29,20 @@ import at.bitfire.davdroid.sync.Syncer
3029
import at.bitfire.davdroid.sync.TaskSyncer
3130
import at.bitfire.davdroid.ui.NotificationRegistry
3231
import at.bitfire.ical4android.TaskProvider
33-
import dagger.hilt.EntryPoint
34-
import dagger.hilt.InstallIn
35-
import dagger.hilt.android.EntryPointAccessors
36-
import dagger.hilt.components.SingletonComponent
3732
import kotlinx.coroutines.CoroutineDispatcher
3833
import kotlinx.coroutines.delay
3934
import kotlinx.coroutines.flow.Flow
4035
import kotlinx.coroutines.flow.map
4136
import kotlinx.coroutines.runInterruptible
4237
import kotlinx.coroutines.withContext
4338
import java.util.Collections
39+
import java.util.logging.Logger
40+
import javax.inject.Inject
41+
import javax.inject.Provider
4442

4543
abstract class BaseSyncWorker(
4644
context: Context,
4745
private val workerParams: WorkerParameters,
48-
private val accountSettingsFactory: AccountSettings.Factory,
49-
private val notificationRegistry: NotificationRegistry,
50-
private val syncConditionsFactory: SyncConditions.Factory,
5146
private val syncDispatcher: CoroutineDispatcher
5247
) : CoroutineWorker(context, workerParams) {
5348

@@ -124,19 +119,29 @@ abstract class BaseSyncWorker(
124119

125120
}
126121

127-
// We don't inject the Syncers in our constructor because that would generate
128-
// every syncer object regardless of whether it's even used for the synced authority (useless overhead).
129-
@EntryPoint
130-
@InstallIn(SingletonComponent::class)
131-
interface BaseSyncWorkerEntryPoint {
132-
fun addressBookSyncer(): AddressBookSyncer
133-
fun calendarSyncer(): CalendarSyncer
134-
fun jtxSyncer(): JtxSyncer
135-
fun taskSyncer(): TaskSyncer
136-
}
137-
private val entryPoint = EntryPointAccessors.fromApplication<BaseSyncWorkerEntryPoint>(context)
122+
@Inject
123+
lateinit var accountSettingsFactory: AccountSettings.Factory
124+
125+
@Inject
126+
lateinit var addressBookSyncer: Provider<AddressBookSyncer>
127+
128+
@Inject
129+
lateinit var calendarSyncer: Provider<CalendarSyncer>
130+
131+
@Inject
132+
lateinit var jtxSyncer: Provider<JtxSyncer>
133+
134+
@Inject
135+
lateinit var logger: Logger
136+
137+
@Inject
138+
lateinit var notificationRegistry: NotificationRegistry
139+
140+
@Inject
141+
lateinit var syncConditionsFactory: SyncConditions.Factory
138142

139-
private val notificationManager = NotificationManagerCompat.from(applicationContext)
143+
@Inject
144+
lateinit var taskSyncer: Provider<TaskSyncer>
140145

141146

142147
override suspend fun doWork(): Result {
@@ -148,10 +153,10 @@ abstract class BaseSyncWorker(
148153
val authority = inputData.getString(INPUT_AUTHORITY) ?: throw IllegalArgumentException("$INPUT_AUTHORITY required")
149154

150155
val syncTag = commonTag(account, authority)
151-
Logger.log.info("${javaClass.simpleName} called for $syncTag")
156+
logger.info("${javaClass.simpleName} called for $syncTag")
152157

153158
if (!runningSyncs.add(syncTag)) {
154-
Logger.log.info("There's already another worker running for $syncTag, skipping")
159+
logger.info("There's already another worker running for $syncTag, skipping")
155160
return Result.success()
156161
}
157162

@@ -160,7 +165,7 @@ abstract class BaseSyncWorker(
160165
accountSettingsFactory.forAccount(account)
161166
} catch (e: InvalidAccountException) {
162167
val workId = workerParams.id
163-
Logger.log.warning("Account $account doesn't exist anymore, cancelling worker $workId")
168+
logger.warning("Account $account doesn't exist anymore, cancelling worker $workId")
164169

165170
val workManager = WorkManager.getInstance(applicationContext)
166171
workManager.cancelWorkById(workId)
@@ -169,26 +174,26 @@ abstract class BaseSyncWorker(
169174
}
170175

171176
if (inputData.getBoolean(INPUT_MANUAL, false))
172-
Logger.log.info("Manual sync, skipping network checks")
177+
logger.info("Manual sync, skipping network checks")
173178
else {
174179
val syncConditions = syncConditionsFactory.create(accountSettings)
175180

176181
// check internet connection
177182
if (!syncConditions.internetAvailable()) {
178-
Logger.log.info("WorkManager started SyncWorker without Internet connection. Aborting.")
183+
logger.info("WorkManager started SyncWorker without Internet connection. Aborting.")
179184
return Result.success()
180185
}
181186

182187
// check WiFi restriction
183188
if (!syncConditions.wifiConditionsMet()) {
184-
Logger.log.info("WiFi conditions not met. Won't run periodic sync.")
189+
logger.info("WiFi conditions not met. Won't run periodic sync.")
185190
return Result.success()
186191
}
187192
}
188193

189194
return doSyncWork(account, authority, accountSettings)
190195
} finally {
191-
Logger.log.info("${javaClass.simpleName} finished for $syncTag")
196+
logger.info("${javaClass.simpleName} finished for $syncTag")
192197
runningSyncs -= syncTag
193198
}
194199
}
@@ -198,19 +203,19 @@ abstract class BaseSyncWorker(
198203
authority: String,
199204
accountSettings: AccountSettings
200205
): Result = withContext(syncDispatcher) {
201-
Logger.log.info("Running ${javaClass.name}: account=$account, authority=$authority")
206+
logger.info("Running ${javaClass.name}: account=$account, authority=$authority")
202207

203208
// What are we going to sync? Select syncer based on authority
204209
val syncer: Syncer = when (authority) {
205210
applicationContext.getString(R.string.address_books_authority) ->
206-
entryPoint.addressBookSyncer()
211+
addressBookSyncer.get()
207212
CalendarContract.AUTHORITY ->
208-
entryPoint.calendarSyncer()
213+
calendarSyncer.get()
209214
TaskProvider.ProviderName.JtxBoard.authority ->
210-
entryPoint.jtxSyncer()
215+
jtxSyncer.get()
211216
TaskProvider.ProviderName.OpenTasks.authority,
212217
TaskProvider.ProviderName.TasksOrg.authority ->
213-
entryPoint.taskSyncer()
218+
taskSyncer.get()
214219
else ->
215220
throw IllegalArgumentException("Invalid authority $authority")
216221
}
@@ -243,21 +248,21 @@ abstract class BaseSyncWorker(
243248

244249
// On soft errors the sync is retried a few times before considered failed
245250
if (result.hasSoftError()) {
246-
Logger.log.warning("Soft error while syncing: result=$result, stats=${result.stats}")
251+
logger.warning("Soft error while syncing: result=$result, stats=${result.stats}")
247252
if (runAttemptCount < MAX_RUN_ATTEMPTS) {
248253
val blockDuration = result.delayUntil - System.currentTimeMillis() / 1000
249-
Logger.log.warning("Waiting for $blockDuration seconds, before retrying ...")
254+
logger.warning("Waiting for $blockDuration seconds, before retrying ...")
250255

251256
// We block the SyncWorker here so that it won't be started by the sync framework immediately again.
252257
// This should be replaced by proper work scheduling as soon as we don't depend on the sync framework anymore.
253258
if (blockDuration > 0)
254259
delay(blockDuration * 1000)
255260

256-
Logger.log.warning("Retrying on soft error (attempt $runAttemptCount of $MAX_RUN_ATTEMPTS)")
261+
logger.warning("Retrying on soft error (attempt $runAttemptCount of $MAX_RUN_ATTEMPTS)")
257262
return@withContext Result.retry()
258263
}
259264

260-
Logger.log.warning("Max retries on soft errors reached ($runAttemptCount of $MAX_RUN_ATTEMPTS). Treating as failed")
265+
logger.warning("Max retries on soft errors reached ($runAttemptCount of $MAX_RUN_ATTEMPTS). Treating as failed")
261266

262267
notificationRegistry.notifyIfPossible(NotificationRegistry.NOTIFY_SYNC_ERROR, tag = softErrorNotificationTag) {
263268
NotificationCompat.Builder(applicationContext, NotificationRegistry.CHANNEL_SYNC_IO_ERRORS)
@@ -275,6 +280,7 @@ abstract class BaseSyncWorker(
275280
}
276281

277282
// If no soft error found, dismiss sync error notification
283+
val notificationManager = NotificationManagerCompat.from(applicationContext)
278284
notificationManager.cancel(
279285
softErrorNotificationTag,
280286
NotificationRegistry.NOTIFY_SYNC_ERROR
@@ -283,7 +289,7 @@ abstract class BaseSyncWorker(
283289
// On a hard error - fail with an error message
284290
// Note: SyncManager should have notified the user
285291
if (result.hasHardError()) {
286-
Logger.log.warning("Hard error while syncing: result=$result, stats=${result.stats}")
292+
logger.warning("Hard error while syncing: result=$result, stats=${result.stats}")
287293
return@withContext Result.failure(syncResult)
288294
}
289295
}

app/src/main/kotlin/at/bitfire/davdroid/sync/worker/OneTimeSyncWorker.kt

+1-4
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,8 @@ import java.util.logging.Level
4545
class OneTimeSyncWorker @AssistedInject constructor(
4646
@Assisted appContext: Context,
4747
@Assisted workerParams: WorkerParameters,
48-
accountSettingsFactory: AccountSettings.Factory,
49-
notificationRegistry: NotificationRegistry,
50-
syncConditionsFactory: SyncConditions.Factory,
5148
syncDispatcher: SyncDispatcher
52-
) : BaseSyncWorker(appContext, workerParams, accountSettingsFactory, notificationRegistry, syncConditionsFactory, syncDispatcher.dispatcher) {
49+
) : BaseSyncWorker(appContext, workerParams, syncDispatcher.dispatcher) {
5350

5451
companion object {
5552

app/src/main/kotlin/at/bitfire/davdroid/sync/worker/PeriodicSyncWorker.kt

+1-7
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ import androidx.work.Operation
1616
import androidx.work.PeriodicWorkRequestBuilder
1717
import androidx.work.WorkManager
1818
import androidx.work.WorkerParameters
19-
import at.bitfire.davdroid.settings.AccountSettings
20-
import at.bitfire.davdroid.sync.SyncConditions
2119
import at.bitfire.davdroid.sync.SyncDispatcher
22-
import at.bitfire.davdroid.ui.NotificationRegistry
2320
import dagger.assisted.Assisted
2421
import dagger.assisted.AssistedInject
2522
import java.util.concurrent.TimeUnit
@@ -44,11 +41,8 @@ import java.util.concurrent.TimeUnit
4441
class PeriodicSyncWorker @AssistedInject constructor(
4542
@Assisted appContext: Context,
4643
@Assisted workerParams: WorkerParameters,
47-
accountSettingsFactory: AccountSettings.Factory,
48-
notificationRegistry: NotificationRegistry,
49-
syncConditionsFactory: SyncConditions.Factory,
5044
syncDispatcher: SyncDispatcher
51-
) : BaseSyncWorker(appContext, workerParams, accountSettingsFactory, notificationRegistry, syncConditionsFactory, syncDispatcher.dispatcher) {
45+
) : BaseSyncWorker(appContext, workerParams, syncDispatcher.dispatcher) {
5246

5347
companion object {
5448

0 commit comments

Comments
 (0)