Repository navigation
Conversation
The settings implementations now read and write a Preferences DataStore through DataStoreSettings, an ObservableSettings that serves reads from the loaded file and saves writes in the background. Writes that arrive while one is saving are folded into the next, so drags no longer write on every frame. StartupInitializer loads the file before anything reads a setting, inside a startup:loadSettings trace section the startup benchmark records. On first launch LegacySettingsMigration copies every settings key out of the old platform preferences, typed from a key table because desktop's java.util.prefs keeps everything as text. The old values stay for a release so a downgrade or an older backup still works. On Android the new file is backed up, and the backup agent also clears the device-bound keys from it after a restore (#607).
Adds :features:settings:benchmark, a Jetpack Microbenchmark module that times opening and reading the settings store the way startup does, with and without migrating the old SharedPreferences settings. The new app.campfire.android.benchmark convention sets up such modules: a release, non-debuggable test build run by AndroidBenchmarkRunner. ./gradlew :features:settings:benchmark:connectedReleaseAndroidTest
StartupInitializer now starts the settings read before it creates the user initializer, and only waits for it right before the session restore. Creating the user initializer's dependencies is most of the main thread's startup work, so the read finishes behind it instead of blocking: on a Galaxy A51 the main thread sat idle for ~11ms of the ~12.6ms load. SettingsLoader gains startLoading(); load() awaits the same read. The startup:loadSettings trace section now covers only the wait.
Generated by 🚫 Danger Kotlin against 9af0dd2 |
Collaborator
This was referenced Oct 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second step of #607, stacked on #1189. Settings move from platform preferences (SharedPreferences / java.util.prefs / NSUserDefaults) to a Preferences DataStore. Nothing above the storage layer changes: the
AppSettingsdelegates, thevar+observeX()API and every call site are untouched.How it works
DataStoreSettingsis anObservableSettingsbacked by DataStore, so the existing delegates keep working. The settings implementations only add a@SettingsStorequalifier to the store they inject.multiplatform-settings-datastoreonly offers a suspendFlowSettings, which can't back synchronous getters, so this wraps DataStore directly.Loading:
SettingsLoaderloads the file once at startup.StartupInitializerstarts the read before it creates the user initializer, whose dependencies are most of the main thread's startup work.Migration:
LegacySettingsMigrationcopies every settings key out of the old preferences on first launch.Files: a single
settings.preferences_pb.files/datastore/~/.config/Campfire/datastore/Android backup: the new file is in
data_extraction_rules.xml, and the old prefs entry stays so restoring an older backup still migrates. After a restore,SettingsBackupAgentalso clears the device-bound keys from the new file.Scope: other modules on SharedPreferences (discover scan, bookinfo, library reconciler, account restore snapshot) are unchanged; their per-user keys move with the per-user stores in the next step.
Benchmark tooling
:features:settings:benchmark: a Jetpack Microbenchmark of loading the store with and without migrating. Run it with./gradlew :features:settings:benchmark:connectedReleaseAndroidTest.app.campfire.android.benchmarkconvention: sets up such modules. It adds theandroidx.benchmarkGradle plugin andbenchmark-junit4, both 1.5.0, matching the existing macrobenchmark version.startup:loadSettings(inStartupTraceSections) is recorded byDiStartupBenchmarks.Verification
DataStoreSettingsTest(10) andDataStoreSettingsFileTest(2, real files on the JVM).LegacySettingsMigrationTest(6)../gradlew jvmTest testpasses exceptinfra/audioplayer/engine-tests, which fails locally because the FFmpeg native library doesn't load (no jniavutil); this PR doesn't touch anything those tests use.:app:desktop:compileKotlinand:app:android:compileAlphaDebugKotlincompile, and the touched modules compile for the iOS simulator.Performance
Galaxy A51 (SM-A515U, Android 13), signed-in cold starts to Home, fully AOT-compiled, 20 launches per run. Numbers come from all traces (Perfetto
android_startups), because Macrobenchmark dropped iterations from its own startup metric on this device.Settings load on the main thread:
startup:loadSettings(main-thread wait)CampfireApplication.onCreateMigration (runs once, on the first launch after updating):
Follow-ups
androidx.datastore:datastore-preferences-core) and a Gradle plugin (androidx.benchmark). Both are Apache-licensed from Google Maven, but I haven't run the F-Droid scan check.No changelog entry: nothing should change for users.
🤖 Generated with Claude Code