Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Essentials - preferences - prevent Application.Quit() from closing application before SharedPreferences in-memory changes are committed to disk. #27635

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

bricefriha
Copy link

@bricefriha bricefriha commented Feb 7, 2025

Description of Change

Currently, if we use Application.Quit() after Preferences.Set(), the data is not saved.
The reason for this (as mentioned in the issue) is that we are using SharedPreferences.Editor.apply(), which write in memory asynchronously, therefore if we run Application.Quit() just after that. We run the issue of exiting the program before the data gets written in memory.

Using SharedPreferences.Editor.Commit()`, on the other hand, ensures the data is being fully saved before we quit.

Changes

Adding an commit argument to the Preferences.Set() API to allow the user to choose whether to use editor.commit() or editor.Apply() at the end of the process.

image.

By default, it will trigger editor.Apply().
This argument is only offered on Android.

Issues Fixed

Fixes #27585

@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Feb 7, 2025
@bricefriha bricefriha marked this pull request as ready for review February 7, 2025 11:13
@bricefriha bricefriha requested a review from a team as a code owner February 7, 2025 11:13
@NathanielJS1541
Copy link

NathanielJS1541 commented Feb 7, 2025

Sorry, I know I'm not a maintainer but wanted to throw in my two cents. I'm not sure changing calls from Apply() to Commit() is the right call here, as it will have a performance impact since it blocks until the preference is written to disk. If apps write lots of preferences in a batch, blocking for each one could have a negative impact on app performance. Remember my repro sees the app go down before a single preference is written, so this degradation of performance could be pretty severe.

I think finding a way to modify Application.Quit() to properly ensure that the Android lifecycle events are serviced (which should take care of waiting for in-memory changes to be committed) would be preferable to changing the way the preferences are saved.

Alternatively, adding something to the MAUI Preferences API like Preferences.Commit() or Preferences.EnsureSaved() could be an option. Then you can keep the performance benefit of using Apply(), and just call Preferences.Commit() before quitting the application to ensure changes are on disk (or enforce a call from Application.Quit() itself).

Edit: Thinking about it, the Preferences.Commit() method doesn't need to be part of the public API if it's only going to be called from within Application.Quit()...

@jsuarezruiz jsuarezruiz added the area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info label Feb 7, 2025
@bricefriha bricefriha marked this pull request as draft February 7, 2025 11:44
@bricefriha
Copy link
Author

@NathanielJS1541 Thanks for your input.
You're right. I'll take a look.

@NathanielJS1541
Copy link

I'll take a look.

It's all good! Sorry to be a pain. I will say that I spent a long time looking for alternatives to Environment.Exit(0) used in Application.Quit() here, but couldn't find anything that seemed to fix #27585.

The only thing I managed to find was this (taken from #27585):

It's also worth noting that calling Application.Quit() doesn't cause the OnDestroy() delegate to be invoked either. Using something like Platform.CurrentActivity.FinishAffinity() does cause the OnDestroy() delegate to be invoked, but it still won't ensure that SharedPreferences edits are committed to disk before the app closes.

I'm not that experienced with android app lifecycles though, so it could be something obvious.

@bricefriha
Copy link
Author

@NathanielJS1541, thanks for the details on that.

It's all good! Sorry to be a pain

Don't worry, I'm enjoying investigating this. So you're not a pain at all.😁

Current.Set<string?>(key, value, sharedName, commit);

/// <inheritdoc cref="IPreferences.Set{T}(string, T, string?, bool)"/>
public static void Set(string key, bool value, string? sharedName, bool commit = false) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note: Adding new public methods is allowed but such a change will most likely not be accepted for MAUI .NET 9. It can be merged in MAUI .NET 10. Just to warn if you want your PR to be merged in the .NET 9 branch.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Even if these are just different signatures?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a part of the MAUI team, so they might tell you differently but if you want my honest opinion based on experience of reading many MAUI's PRs, then: Yes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I appreciate you're sharing your opinion.

Well, we'll see, I just can't see how we can solve this issue otherwise if I'm honest.

@bricefriha
Copy link
Author

@dotnet-policy-service agree

@bricefriha bricefriha marked this pull request as ready for review February 11, 2025 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info community ✨ Community Contribution
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Application.Quit() closes application before SharedPreferences in-memory changes are committed to disk.
4 participants