refactor: simplify background worker (#21558)

* chore: log hash starting

* chore: android - bump the min worker delay

* remove local sync only task and always enqueue background workers

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong
2025-09-03 20:27:30 +05:30
committed by GitHub
parent 2f1385a236
commit 9d3f10372d
15 changed files with 266 additions and 542 deletions

View File

@@ -61,9 +61,8 @@ private open class BackgroundWorkerPigeonCodec : StandardMessageCodec() {
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface BackgroundWorkerFgHostApi {
fun enableSyncWorker()
fun enableUploadWorker()
fun disableUploadWorker()
fun enable()
fun disable()
companion object {
/** The codec used by BackgroundWorkerFgHostApi. */
@@ -75,11 +74,11 @@ interface BackgroundWorkerFgHostApi {
fun setUp(binaryMessenger: BinaryMessenger, api: BackgroundWorkerFgHostApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableSyncWorker$separatedMessageChannelSuffix", codec)
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enable$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
api.enableSyncWorker()
api.enable()
listOf(null)
} catch (exception: Throwable) {
BackgroundWorkerPigeonUtils.wrapError(exception)
@@ -91,27 +90,11 @@ interface BackgroundWorkerFgHostApi {
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.enableUploadWorker$separatedMessageChannelSuffix", codec)
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disable$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
api.enableUploadWorker()
listOf(null)
} catch (exception: Throwable) {
BackgroundWorkerPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFgHostApi.disableUploadWorker$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
api.disableUploadWorker()
api.disable()
listOf(null)
} catch (exception: Throwable) {
BackgroundWorkerPigeonUtils.wrapError(exception)
@@ -182,23 +165,6 @@ class BackgroundWorkerFlutterApi(private val binaryMessenger: BinaryMessenger, p
BackgroundWorkerPigeonCodec()
}
}
fun onLocalSync(maxSecondsArg: Long?, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.immich_mobile.BackgroundWorkerFlutterApi.onLocalSync$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(maxSecondsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(BackgroundWorkerPigeonUtils.createConnectionError(channelName)))
}
}
}
fun onIosUpload(isRefreshArg: Boolean, maxSecondsArg: Long?, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""

View File

@@ -16,11 +16,6 @@ import io.flutter.embedding.engine.loader.FlutterLoader
private const val TAG = "BackgroundWorker"
enum class BackgroundTaskType {
LOCAL_SYNC,
UPLOAD,
}
class BackgroundWorker(context: Context, params: WorkerParameters) :
ListenableWorker(context, params), BackgroundWorkerBgHostApi {
private val ctx: Context = context.applicationContext
@@ -84,13 +79,7 @@ class BackgroundWorker(context: Context, params: WorkerParameters) :
* This method acts as a bridge between the native Android background task system and Flutter.
*/
override fun onInitialized() {
val taskTypeIndex = inputData.getInt(BackgroundWorkerApiImpl.WORKER_DATA_TASK_TYPE, 0)
val taskType = BackgroundTaskType.entries[taskTypeIndex]
when (taskType) {
BackgroundTaskType.LOCAL_SYNC -> flutterApi?.onLocalSync(null) { handleHostResult(it) }
BackgroundTaskType.UPLOAD -> flutterApi?.onAndroidUpload { handleHostResult(it) }
}
flutterApi?.onAndroidUpload { handleHostResult(it) }
}
override fun close() {

View File

@@ -3,10 +3,8 @@ package app.alextran.immich.background
import android.content.Context
import android.provider.MediaStore
import android.util.Log
import androidx.core.content.edit
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
@@ -16,18 +14,13 @@ private const val TAG = "BackgroundUploadImpl"
class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
private val ctx: Context = context.applicationContext
override fun enableSyncWorker() {
override fun enable() {
enqueueMediaObserver(ctx)
Log.i(TAG, "Scheduled media observer")
}
override fun enableUploadWorker() {
updateUploadEnabled(ctx, true)
Log.i(TAG, "Scheduled background upload tasks")
}
override fun disableUploadWorker() {
updateUploadEnabled(ctx, false)
override fun disable() {
WorkManager.getInstance(ctx).cancelUniqueWork(OBSERVER_WORKER_NAME)
WorkManager.getInstance(ctx).cancelUniqueWork(BACKGROUND_WORKER_NAME)
Log.i(TAG, "Cancelled background upload tasks")
}
@@ -36,25 +29,14 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
private const val BACKGROUND_WORKER_NAME = "immich/BackgroundWorkerV1"
private const val OBSERVER_WORKER_NAME = "immich/MediaObserverV1"
const val WORKER_DATA_TASK_TYPE = "taskType"
const val SHARED_PREF_NAME = "Immich::Background"
const val SHARED_PREF_BACKUP_ENABLED = "Background::backup::enabled"
private fun updateUploadEnabled(context: Context, enabled: Boolean) {
context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE).edit {
putBoolean(SHARED_PREF_BACKUP_ENABLED, enabled)
}
}
fun enqueueMediaObserver(ctx: Context) {
val constraints = Constraints.Builder()
.addContentUriTrigger(MediaStore.Images.Media.INTERNAL_CONTENT_URI, true)
.addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true)
.addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true)
.addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true)
.setTriggerContentUpdateDelay(5, TimeUnit.SECONDS)
.setTriggerContentMaxDelay(1, TimeUnit.MINUTES)
.setTriggerContentUpdateDelay(30, TimeUnit.SECONDS)
.setTriggerContentMaxDelay(3, TimeUnit.MINUTES)
.build()
val work = OneTimeWorkRequest.Builder(MediaObserver::class.java)
@@ -66,15 +48,13 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
Log.i(TAG, "Enqueued media observer worker with name: $OBSERVER_WORKER_NAME")
}
fun enqueueBackgroundWorker(ctx: Context, taskType: BackgroundTaskType) {
fun enqueueBackgroundWorker(ctx: Context) {
val constraints = Constraints.Builder().setRequiresBatteryNotLow(true).build()
val data = Data.Builder()
data.putInt(WORKER_DATA_TASK_TYPE, taskType.ordinal)
val work = OneTimeWorkRequest.Builder(BackgroundWorker::class.java)
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES)
.setInputData(data.build()).build()
.build()
WorkManager.getInstance(ctx)
.enqueueUniqueWork(BACKGROUND_WORKER_NAME, ExistingWorkPolicy.REPLACE, work)

View File

@@ -6,29 +6,17 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
class MediaObserver(context: Context, params: WorkerParameters) : Worker(context, params) {
private val ctx: Context = context.applicationContext
private val ctx: Context = context.applicationContext
override fun doWork(): Result {
Log.i("MediaObserver", "Content change detected, starting background worker")
override fun doWork(): Result {
Log.i("MediaObserver", "Content change detected, starting background worker")
// Re-enqueue itself to listen for future changes
BackgroundWorkerApiImpl.enqueueMediaObserver(ctx)
// Enqueue backup worker only if there are new media changes
if (triggeredContentUris.isNotEmpty()) {
val type =
if (isBackupEnabled(ctx)) BackgroundTaskType.UPLOAD else BackgroundTaskType.LOCAL_SYNC
BackgroundWorkerApiImpl.enqueueBackgroundWorker(ctx, type)
}
// Re-enqueue itself to listen for future changes
BackgroundWorkerApiImpl.enqueueMediaObserver(ctx)
return Result.success()
}
private fun isBackupEnabled(context: Context): Boolean {
val prefs =
context.getSharedPreferences(
BackgroundWorkerApiImpl.SHARED_PREF_NAME,
Context.MODE_PRIVATE
)
return prefs.getBoolean(BackgroundWorkerApiImpl.SHARED_PREF_BACKUP_ENABLED, false)
// Enqueue backup worker only if there are new media changes
if (triggeredContentUris.isNotEmpty()) {
BackgroundWorkerApiImpl.enqueueBackgroundWorker(ctx)
}
return Result.success()
}
}