From 4a9d80298b62dab8dc4061496d520e8405d34765 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Thu, 27 Feb 2025 23:31:36 +0530 Subject: [PATCH] fix(mobile): bootstrap store inside isolates (#16392) fix: bootstrap store inside isolates Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- mobile/analysis_options.yaml | 1 + .../test_utils/general_helper.dart | 5 +- mobile/lib/main.dart | 42 ++-------------- mobile/lib/services/background.service.dart | 5 +- .../services/backup_verification.service.dart | 3 ++ mobile/lib/utils/bootstrap.dart | 50 +++++++++++++++++++ 6 files changed, 63 insertions(+), 43 deletions(-) create mode 100644 mobile/lib/utils/bootstrap.dart diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index dd081be64e..c8ed225ce5 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -76,6 +76,7 @@ custom_lint: - lib/routing/router.dart - lib/services/immich_logger.service.dart # not really a service... more a util - lib/utils/{db,migration}.dart + - lib/utils/bootstrap.dart - lib/widgets/asset_grid/asset_grid_data_structure.dart - test/**.dart # refactor the remaining providers diff --git a/mobile/integration_test/test_utils/general_helper.dart b/mobile/integration_test/test_utils/general_helper.dart index a3db2d49a8..8e17bae9d3 100644 --- a/mobile/integration_test/test_utils/general_helper.dart +++ b/mobile/integration_test/test_utils/general_helper.dart @@ -7,8 +7,8 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/main.dart' as app; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:isar/isar.dart'; // ignore: depend_on_referenced_packages import 'package:meta/meta.dart'; @@ -39,7 +39,8 @@ class ImmichTestHelper { static Future loadApp(WidgetTester tester) async { await EasyLocalization.ensureInitialized(); // Clear all data from Isar (reuse existing instance if available) - final db = Isar.getInstance() ?? await app.loadDb(); + final db = await Bootstrap.initIsar(); + await Bootstrap.initDomain(db); await Store.clear(); await db.writeTxn(() => db.clear()); // Load main Widget diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 822d772278..da84a8cff6 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -10,20 +10,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/locales.dart'; -import 'package:immich_mobile/domain/services/store.service.dart'; -import 'package:immich_mobile/entities/album.entity.dart'; -import 'package:immich_mobile/entities/android_device_asset.entity.dart'; -import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/entities/backup_album.entity.dart'; -import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; -import 'package:immich_mobile/entities/etag.entity.dart'; -import 'package:immich_mobile/entities/exif_info.entity.dart'; -import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; -import 'package:immich_mobile/entities/logger_message.entity.dart'; -import 'package:immich_mobile/entities/user.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; -import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/providers/app_life_cycle.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/share_intent_upload.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; @@ -37,19 +24,19 @@ import 'package:immich_mobile/services/immich_logger.service.dart'; import 'package:immich_mobile/services/local_notification.service.dart'; import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/theme/theme_data.dart'; +import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/cache/widgets_binding.dart'; import 'package:immich_mobile/utils/download.dart'; import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; import 'package:immich_mobile/utils/migration.dart'; import 'package:intl/date_symbol_data_local.dart'; -import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; -import 'package:path_provider/path_provider.dart'; import 'package:timezone/data/latest.dart'; void main() async { ImmichWidgetsBinding(); - final db = await loadDb(); + final db = await Bootstrap.initIsar(); + await Bootstrap.initDomain(db); await initApp(); await migrateDatabaseIfNeeded(db); HttpOverrides.global = HttpSSLCertOverride(); @@ -122,29 +109,6 @@ Future initApp() async { await FileDownloader().trackTasks(); } -Future loadDb() async { - final dir = await getApplicationDocumentsDirectory(); - Isar db = await Isar.open( - [ - StoreValueSchema, - ExifInfoSchema, - AssetSchema, - AlbumSchema, - UserSchema, - BackupAlbumSchema, - DuplicatedAssetSchema, - LoggerMessageSchema, - ETagSchema, - if (Platform.isAndroid) AndroidDeviceAssetSchema, - if (Platform.isIOS) IOSDeviceAssetSchema, - ], - directory: dir.path, - maxSizeMiB: 1024, - ); - await StoreService.init(storeRepository: IsarStoreRepository(db)); - return db; -} - class ImmichApp extends ConsumerStatefulWidget { const ImmichApp({super.key}); diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index 2a7bfb2bb4..4e83d1f0ac 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -15,7 +15,6 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/interfaces/backup_album.interface.dart'; -import 'package:immich_mobile/main.dart'; import 'package:immich_mobile/models/backup/backup_candidate.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/backup/error_upload_asset.model.dart'; @@ -48,6 +47,7 @@ import 'package:immich_mobile/services/network.service.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:immich_mobile/services/user.service.dart'; import 'package:immich_mobile/utils/backup_progress.dart'; +import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; import 'package:network_info_plus/network_info_plus.dart'; @@ -369,7 +369,8 @@ class BackgroundService { } Future _onAssetsChanged() async { - final db = await loadDb(); + final db = await Bootstrap.initIsar(); + await Bootstrap.initDomain(db); HttpOverrides.global = HttpSSLCertOverride(); ApiService apiService = ApiService(); diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index 5938cd7813..0d47d1a111 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -16,6 +16,7 @@ import 'package:immich_mobile/repositories/asset.repository.dart'; import 'package:immich_mobile/repositories/exif_info.repository.dart'; import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/diff.dart'; /// Finds duplicates originating from missing EXIF information @@ -123,6 +124,8 @@ class BackupVerificationService { assert(tuple.deleteCandidates.length == tuple.originals.length); final List result = []; BackgroundIsolateBinaryMessenger.ensureInitialized(tuple.rootIsolateToken); + final db = await Bootstrap.initIsar(); + await Bootstrap.initDomain(db); await tuple.fileMediaRepository.enableBackgroundAccess(); final ApiService apiService = ApiService(); apiService.setEndpoint(tuple.endpoint); diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart new file mode 100644 index 0000000000..32bdac42d5 --- /dev/null +++ b/mobile/lib/utils/bootstrap.dart @@ -0,0 +1,50 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:immich_mobile/domain/services/store.service.dart'; +import 'package:immich_mobile/entities/album.entity.dart'; +import 'package:immich_mobile/entities/android_device_asset.entity.dart'; +import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/entities/backup_album.entity.dart'; +import 'package:immich_mobile/entities/duplicated_asset.entity.dart'; +import 'package:immich_mobile/entities/etag.entity.dart'; +import 'package:immich_mobile/entities/exif_info.entity.dart'; +import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; +import 'package:immich_mobile/entities/logger_message.entity.dart'; +import 'package:immich_mobile/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; +import 'package:isar/isar.dart'; +import 'package:path_provider/path_provider.dart'; + +abstract final class Bootstrap { + static Future initIsar() async { + if (Isar.getInstance() != null) { + return Isar.getInstance()!; + } + + final dir = await getApplicationDocumentsDirectory(); + return await Isar.open( + [ + StoreValueSchema, + ExifInfoSchema, + AssetSchema, + AlbumSchema, + UserSchema, + BackupAlbumSchema, + DuplicatedAssetSchema, + LoggerMessageSchema, + ETagSchema, + if (Platform.isAndroid) AndroidDeviceAssetSchema, + if (Platform.isIOS) IOSDeviceAssetSchema, + ], + directory: dir.path, + maxSizeMiB: 1024, + inspector: kDebugMode, + ); + } + + static Future initDomain(Isar db) async { + await StoreService.init(storeRepository: IsarStoreRepository(db)); + } +}