Skip to content

Latest commit

 

History

History
119 lines (90 loc) · 6.52 KB

File metadata and controls

119 lines (90 loc) · 6.52 KB

Android Persistence (SharedPreferences)

Why the library persists data

Android foreground services can be stopped and restarted by the system (process death, reboot, app update, auto-restart after crash). The library persists service state and configuration to SharedPreferences so the service can be reconstructed without Dart involvement.

Named preference files

The library uses named SharedPreferences files with the prefix com.pravera.flutter_foreground_task.prefs. and never touches the app's default preferences (PreferenceManager.getDefaultSharedPreferences). Each logical group of data gets its own XML file on disk:

Preference file name Contents
...prefs.NOTIFICATION_PERMISSION_STATUS Previous notification permission outcomes (for permanent-denial detection)
...prefs.FOREGROUND_SERVICE_STATUS Which lifecycle action the service should perform next
...prefs.FOREGROUND_SERVICE_TYPES Foreground service type bitflags (API 29+)
...prefs.NOTIFICATION_OPTIONS Notification channel settings and visible content (title, text, icon, buttons, initial route)
...prefs.FOREGROUND_TASK_OPTIONS Task event action, lifecycle flags (autoRunOnBoot, allowWakeLock, etc.), stopWithTask, and callback handle

Keys written

All key constants are defined in PreferencesKey.kt.

Service status (FOREGROUND_SERVICE_STATUS)

Key Type Purpose
foregroundServiceAction String Lifecycle action (API_START, API_STOP, API_RESTART, API_UPDATE, REBOOT, RESTART)

Service types (FOREGROUND_SERVICE_TYPES)

Key Type Purpose
serviceTypes Int Bitwise OR of ServiceInfo.FOREGROUND_SERVICE_TYPE_* flags

Notification options and content (NOTIFICATION_OPTIONS)

Key Type Purpose
serviceId Int Notification/service ID
notificationId Int Legacy fallback for service ID
notificationChannelId String Channel ID
notificationChannelName String Channel display name
notificationChannelDescription String? Channel description
notificationChannelImportance Int Channel importance level
notificationPriority Int Priority for pre-O devices
enableVibration Boolean Vibration toggle
playSound Boolean Sound toggle
showWhen Boolean Timestamp visibility
showBadge Boolean Badge visibility
onlyAlertOnce Boolean Alert-once flag
visibility Int Lock screen visibility
notificationContentTitle String Notification title
notificationContentText String Notification body
icon String (JSON) Custom icon metadata
buttons String (JSON array) Action buttons
initialRoute String? Flutter route on notification tap

Task options and data (FOREGROUND_TASK_OPTIONS)

Key Type Purpose
taskEventAction String (JSON) Event type and interval
interval Long Deprecated: repeat interval
isOnceEvent Boolean Deprecated: one-shot flag
autoRunOnBoot Boolean Restart on device boot
autoRunOnMyPackageReplaced Boolean Restart after app update
allowWakeLock Boolean Acquire partial wake lock
allowWifiLock Boolean Acquire Wi-Fi lock
allowAutoRestart Boolean Auto-restart after crash
stopWithTask Boolean? Stop when task is removed
callbackHandle Long Dart isolate callback handle

Notification permission (NOTIFICATION_PERMISSION_STATUS)

Key Type Purpose
(permission string) String Previous grant/deny result for permanent-denial detection

Multi-service scoping

When using multiple services, each service id gets its own set of preference files. The file name is constructed as:

  • Default service ("default"): prefix + name — e.g. com.pravera.flutter_foreground_task.prefs.NOTIFICATION_OPTIONS. This is identical to the legacy single-service layout, so existing installs keep their data with zero migration.
  • Custom service id: prefix + serviceId + "." + name — e.g. com.pravera.flutter_foreground_task.prefs.locationTracker.NOTIFICATION_OPTIONS.

This isolation is handled by ForegroundTaskStorageProvider.getPreferences(context, serviceId, name). The legacy two-argument overload getPreferences(context, name) defaults to "default" for backward compatibility.

Architecture

All persistence goes through model companion objects (getData, setData, updateData, clearData) backed by a ForegroundTaskStorageProvider. The provider returns SharedPreferences instances for each named file and can be reconfigured with a custom prefix from Dart. All model companions accept an optional serviceId parameter (defaulting to "default") to scope reads and writes.

Dart (AndroidNotificationOptions)
  └─ method channel ──► ForegroundServiceManager.start(serviceId, ...)
                            └─ Model.setData(context, serviceId, args)
                                  └─ ForegroundTaskStorageProvider.getPreferences(context, serviceId, name)
                                        └─ context.getSharedPreferences(scopedName, MODE_PRIVATE)

Direct SharedPreferences reads also occur in:

  • FlutterForegroundServiceBase.onStartCommand -- reads stopWithTask to install visibility tracking (inherited by ForegroundService and any user subclass)
  • ForegroundServiceUtils.isSetStopWithTaskFlag -- reads stopWithTask for reboot/restart receivers (now takes a serviceId argument)

Comparison with iOS

Aspect Android iOS
Storage Named SharedPreferences files UserDefaults with custom suite name
Namespace isolation Fully prefixed file names Separate suite (com.pravera.flutter_foreground_task)
Default preferences pollution None None (after refactor)
Abstraction ForegroundTaskStorageProvider ForegroundTaskStorage protocol + ForegroundTaskStorageProvider
Dart configuration storagePrefix on AndroidNotificationOptions storageSuiteName on IOSNotificationOptions

Dart-side storage (SharedPreferences)

Separately from the Android-side persistence described above, the library exposes a Dart storage API (FlutterForegroundTask.saveData, getData, etc.) backed by SharedPreferences. Those keys are prefixed with com.pravera.flutter_foreground_task.prefs. and are unrelated to the native-side persistence documented here.