Улучшена фоновая синхронизация, обработана ошибка
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
@@ -72,6 +73,14 @@
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="dataSync"
|
||||
android:description="@string/fgs_task_pipeline_description" />
|
||||
|
||||
<receiver
|
||||
android:name=".sync.StorageSyncBootReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.github.nullptroma.wallenc.app.di
|
||||
|
||||
import com.github.nullptroma.wallenc.app.sync.StorageSyncScheduler
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
@EntryPoint
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface StorageSyncBootEntryPoint {
|
||||
fun storageSyncScheduler(): StorageSyncScheduler
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.github.nullptroma.wallenc.app.sync
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.github.nullptroma.wallenc.app.di.StorageSyncBootEntryPoint
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
import timber.log.Timber
|
||||
|
||||
class StorageSyncBootReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
if (intent?.action != Intent.ACTION_BOOT_COMPLETED) {
|
||||
return
|
||||
}
|
||||
val scheduler = EntryPointAccessors.fromApplication(
|
||||
context.applicationContext,
|
||||
StorageSyncBootEntryPoint::class.java,
|
||||
).storageSyncScheduler()
|
||||
scheduler.ensureScheduled()
|
||||
Timber.d("Rescheduled periodic storage sync after boot")
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.github.nullptroma.wallenc.app.sync
|
||||
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.StorageSyncPaths
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageSyncGroupStore
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
|
||||
import com.github.nullptroma.wallenc.domain.tasks.StorageSyncTriggerReason
|
||||
import com.github.nullptroma.wallenc.usecases.RunStorageSyncUseCase
|
||||
import com.github.nullptroma.wallenc.usecases.StorageSyncReadiness
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
@@ -11,10 +13,13 @@ import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -24,11 +29,15 @@ class StorageSyncBootstrap @Inject constructor(
|
||||
private val scheduler: StorageSyncScheduler,
|
||||
private val vaultsManager: IVaultsManager,
|
||||
private val syncRunner: RunStorageSyncUseCase,
|
||||
private val syncReadiness: StorageSyncReadiness,
|
||||
private val groupStore: IStorageSyncGroupStore,
|
||||
) {
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
private val startupSyncScheduled = AtomicBoolean(false)
|
||||
|
||||
fun start() {
|
||||
scheduler.ensureScheduled()
|
||||
scheduleStartupSyncOnce()
|
||||
scope.launch {
|
||||
combine(
|
||||
vaultsManager.allStorages,
|
||||
@@ -77,4 +86,31 @@ class StorageSyncBootstrap @Inject constructor(
|
||||
}
|
||||
return System.currentTimeMillis() >= syncRunner.debounceSuppressUntilMs.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Одна синхронизация после готовности хранилищ при старте процесса — не ждать только WorkManager
|
||||
* (особенно если periodic work откладывался из‑за перезапусков процесса).
|
||||
*/
|
||||
private fun scheduleStartupSyncOnce() {
|
||||
scope.launch {
|
||||
combine(
|
||||
vaultsManager.allStorages,
|
||||
vaultsManager.unlockManager.openedStorages,
|
||||
) { rootStorages, opened ->
|
||||
(rootStorages + opened.values).distinctBy { it.uuid }
|
||||
}
|
||||
.map { it.isNotEmpty() }
|
||||
.distinctUntilChanged()
|
||||
.filter { it }
|
||||
.first()
|
||||
if (!startupSyncScheduled.compareAndSet(false, true)) {
|
||||
return@launch
|
||||
}
|
||||
if (groupStore.getGroups().isEmpty()) {
|
||||
return@launch
|
||||
}
|
||||
syncReadiness.awaitReady()
|
||||
syncRunner.enqueue(StorageSyncTriggerReason.Background)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,10 @@ class StorageSyncScheduler @Inject constructor(
|
||||
)
|
||||
.build()
|
||||
|
||||
// KEEP: UPDATE сбрасывает таймер periodic work при каждом onCreate процесса.
|
||||
WorkManager.getInstance(app).enqueueUniquePeriodicWork(
|
||||
StorageSyncWorker.UNIQUE_WORK_NAME,
|
||||
ExistingPeriodicWorkPolicy.UPDATE,
|
||||
ExistingPeriodicWorkPolicy.KEEP,
|
||||
request,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user