fix: ensure background worker is scheduled when the app is dismissed (#22032)

* fix: ensure background worker is scheduled when the app is dismissed

* remove logs

* fix: use native locks (#22081)

* fix: native locks

* use atomicints

* change count check

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>

---------

Co-authored-by: shenlong <139912620+shenlong-tanwen@users.noreply.github.com>
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
Alex 2025-09-17 09:11:55 -05:00 committed by GitHub
parent 2e945281fc
commit b2ca208dbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 45 additions and 2 deletions

View File

@ -3,6 +3,7 @@ package app.alextran.immich
import android.app.Application
import androidx.work.Configuration
import androidx.work.WorkManager
import app.alextran.immich.background.BackgroundWorkerApiImpl
class ImmichApp : Application() {
override fun onCreate() {
@ -14,6 +15,8 @@ class ImmichApp : Application() {
// Thus, the BackupWorker is not started. If the system kills the process after each initialization
// (because of low memory etc.), the backup is never performed.
// As a workaround, we also run a backup check when initializing the application
ContentObserverWorker.startBackupWorker(context = this, delayMilliseconds = 0)
BackgroundWorkerApiImpl.enqueueBackgroundWorker(this)
}
}

View File

@ -3,6 +3,7 @@ package app.alextran.immich
import android.content.Context
import android.os.Build
import android.os.ext.SdkExtensions
import app.alextran.immich.background.BackgroundEngineLock
import app.alextran.immich.background.BackgroundWorkerApiImpl
import app.alextran.immich.background.BackgroundWorkerFgHostApi
import app.alextran.immich.connectivity.ConnectivityApi
@ -25,6 +26,7 @@ class MainActivity : FlutterFragmentActivity() {
fun registerPlugins(ctx: Context, flutterEngine: FlutterEngine) {
flutterEngine.plugins.add(BackgroundServicePlugin())
flutterEngine.plugins.add(HttpSSLOptionsPlugin())
flutterEngine.plugins.add(BackgroundEngineLock())
val messenger = flutterEngine.dartExecutor.binaryMessenger
val nativeSyncApiImpl =

View File

@ -0,0 +1,33 @@
package app.alextran.immich.background
import android.util.Log
import androidx.work.WorkManager
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.plugins.FlutterPlugin
import java.util.concurrent.atomic.AtomicInteger
private const val TAG = "BackgroundEngineLock"
class BackgroundEngineLock : FlutterPlugin {
companion object {
const val ENGINE_CACHE_KEY = "immich::background_worker::engine"
var engineCount = AtomicInteger(0)
}
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
// work manager task is running while the main app is opened, cancel the worker
if (engineCount.incrementAndGet() > 1 && FlutterEngineCache.getInstance()
.get(ENGINE_CACHE_KEY) != null
) {
WorkManager.getInstance(binding.applicationContext)
.cancelUniqueWork(BackgroundWorkerApiImpl.BACKGROUND_WORKER_NAME)
FlutterEngineCache.getInstance().remove(ENGINE_CACHE_KEY)
}
Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount")
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
engineCount.decrementAndGet()
Log.i(TAG, "Flutter engine detached. Attached Engines count: $engineCount")
}
}

View File

@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture
import io.flutter.FlutterInjector
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.embedding.engine.loader.FlutterLoader
import java.util.concurrent.TimeUnit
@ -75,6 +76,9 @@ class BackgroundWorker(context: Context, params: WorkerParameters) :
loader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) {
engine = FlutterEngine(ctx)
FlutterEngineCache.getInstance().remove(BackgroundEngineLock.ENGINE_CACHE_KEY);
FlutterEngineCache.getInstance()
.put(BackgroundEngineLock.ENGINE_CACHE_KEY, engine!!)
// Register custom plugins
MainActivity.registerPlugins(ctx, engine!!)
@ -188,6 +192,7 @@ class BackgroundWorker(context: Context, params: WorkerParameters) :
isComplete = true
engine?.destroy()
engine = null
FlutterEngineCache.getInstance().remove(BackgroundEngineLock.ENGINE_CACHE_KEY);
flutterApi = null
notificationManager.cancel(NOTIFICATION_ID)
waitForForegroundPromotion()

View File

@ -26,7 +26,7 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
}
companion object {
private const val BACKGROUND_WORKER_NAME = "immich/BackgroundWorkerV1"
const val BACKGROUND_WORKER_NAME = "immich/BackgroundWorkerV1"
private const val OBSERVER_WORKER_NAME = "immich/MediaObserverV1"
fun enqueueMediaObserver(ctx: Context) {
@ -56,7 +56,7 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(ctx)
.enqueueUniqueWork(BACKGROUND_WORKER_NAME, ExistingWorkPolicy.REPLACE, work)
.enqueueUniqueWork(BACKGROUND_WORKER_NAME, ExistingWorkPolicy.KEEP, work)
Log.i(TAG, "Enqueued background worker with name: $BACKGROUND_WORKER_NAME")
}