Hey, I notice update to preferences take around 0-...
# android-architecture
u
Hey, I notice update to preferences take around 0-1ms, how is this possible with disc io? If it only updates some memcache, which is only later synchronized to disc async-ly; does it mean its not as durable as a sqlite would?
s
It is only that fast when calling
apply
. When using
commit
it probably will be slower. When calling
apply
, it applies it first to memory only and then immediately kicks of a background task to properly commit it to local persistent storage.
g
does it mean its not as durable as a sqlite would
It’s definitely not as durable as sqlite
u
damn so my state might not be there after process kill?
I mean.. I struggled to pick if I should save to prefs or a sqlite, since my data is not normalized at all, and I always just basically select *;
s
The chance of your preference data not being committed is very very very small.
u
but it is there, unlike sqlite, also .. no transactions
s
Shared Preferences is for user preferences and such, a simple key-value store. It should not be used as a proper db. 🙂
u
so, youd just rather have one table of one row where the value is json?
I have lot of 1:1 relations
s
Is it for HTTP caching?
u
not really, think onboarding flow where each screen produces some data and flow collects them
or .. user has lots of 1 row structures, like id.. profile, usage stats, etc. in proper sql it would be 1:1
I felt its a overkill if you dont have more than more row in a given table
user+profile is good simplified example
s
Ah… You could just put it all in memory. But if you want to be able to continue properly after a process kill and restart, then shared preference is good enough. In the very very small chance that the commit didn’t take place, the user would start a step earlier than where they left off.
u
yes it needs persisting
but, its not all a single object, i have "user" key, "profile" key, etc.. so not a real transaction 😟
s
Ah.. A user-object being created/configured while the user is onboarding. This user-object is then used by the rest of the app after onboarding?
u
lets say yes, i just made it up, that the model looks mostly like user has settings object, usage stats object, profile details object, etc, 10 of these
so, does it make sense to create 10 tables of 1 row, each with a FK to user table, or always 1 row also?
s
You have multiple users supported by your app?
u
no just one, data is not deep but very wide
also its a multi module multiapp monorepo
s
multi platform app (Android, iOS ,etc?)
Or a bunch of android apps with many modules?
Since you don’t really have a hierarchy, just a set of heterogenous objects (stats, details, etc) belonging to the current user of the app, I’d just store the JSON strings in SharedPrefs. Since there is only one user, no foreign keys are needed and such…. just a bunch of documents describing the stats, details, etc. JSON strings is SharedPreferences should suffice.
u
just android apps, lets say app2 doesnt include userProfile
it only includes :state, :settings, each with their own dao and a key to prefs, so prefs look like this at runtime <prefs> < .. "user" .. = { .. json} /> < .. "profile" .. = { .. json} /> < .. "stats .. = { .. json} /> < .. "settings.. = { .. json} /> </prefs
and with this setup, I worry updates not being atomic, when inserting all 4 at once after logging in
but also I dont want a giant User object containg those 3, perf-wise
s
It seems you don’t need relations between the objects… they are just flat objects (documents) pertaining to the app globally to the currently logged in user. With your <prefs> snippet, it looks like that. I’d just store them as string in SharedPreferences, each with a unique key-name. Don’t overthink the non-atomicity of SharedPreferences. When you do
Copy code
sharedPrefs.edit()
    .putString(USER, user.toJsonString())
    .putString(PROFILE, userProfile.toJsonString())
    ...
    .apply()
Then either the entire change between
edit()
and
apply()
will get committed or not (veeeeeery small chance it’d fail)
u
oh so if I use the same editor object it will act as a atomic transaction?
s
Yup. It is not db-grade guarantee (no rollback if something goes truly wrong, some odd exception, veeeery small chance), but good enough.
u
so would you do that in a multi module world, since my daos basically look like this
Copy code
SettingsDao.saveSettings(settings: Settings) {
   prefs.edit().putString("settings", settings.toJson()
}
ProfileDao.saveProfile(profile: Profile) {
   prefs.edit().putString("profile", profile.toJson()
}


-....
fun sync() {
    settingsDao.saveSettings(settings)
    profileDao.saveProfile(profile)
}
id need to pass the editor to all save methods hmm
well if it throws and it doesnt rollback, then what, it commits partial data?
s
Why would it crash/throw? If it crashes because there is no storage left on the device to save the prefs…. I’ve never seen that happen.
u
Idk, some..reason, if it doesn do that, I dont need transactions, dont it?
s
In other words, don’t start coding for something that is veeeeery unlikely to happen. In theory, a
putString
or an
apply
call to the SharedPreference’s Editor can happen… but it is not worth fretting over.
u
btw does the size of prefs matter? I do have the odd type that is a large list of products, it never changes, just syncs from time to time
does it make sense to separate out into its own products_prefs.xml?
s
I would create a different SharedPreferences with a different name. SharedPreferences are stored in XML files. They have no real size limit… still…. I’d use different SharedPreference files…
u
right, but I presume I cannot use the same editor instance to transaction-save all the stuff at once
s
No, you are correct. They’d use a different
Editor
object.
u
dammit
I would switch to db maybe, mostly for transactions and ON DELETE CASCADE, but, im not sure if databases can work in multimodule
and if I have to have db per module, then prefs are just as if not better
s
If you inject your SharedPreferences instances (or SQLite instances) into your modules, then it would work in multi-module projects. But you can’t (easily) share SharedPReferences nor SQLite dbs across multiple apps.
u
but each module basically defines a table, as I said Settings object/table lives in :settings module, and it'd need a reference to :user as to reference the user table for fk
s
Ah… I thought there is only at most one user, not more than one.
If there are relations between the objects, then I’d recommend an SQLite db. And you still can store the object as a String (JSON).
u
there is one user at one time, usual login
just that the settings, profile, and all that crap, belongs basically to the logged in user, so I like to just delete the user and all his crap gets deelted
s
Have your modules each implement an interface that has a method that takes an
Editor
and have them make modifications to that editor. Then your app calls those methods for each module with an
Editor
and after all these calls are done, call
apply
on it.
u
yea that could work, but those products in another prefs would not -.-
s
And these methods could be to create, change or clear their particular key(s) from the
Editor
that is passed.
Yup, if you use two SharedPreferences, you’d have two Editor instances…. these won’t be applied and committed atomically.
Still, calling
editor1.apply()
and then
editor2.apply()
a little later… what is the chance of a crash happening when those two are called. What are the situations where that could happen and their likelyhood.
u
well, I dont know -.-, is that fear irrational? 😄
s
Not irrational, but an app does not maintain a critical db of user-data that is shared between thousands of users 🙂 If the calls to
apply
for some reason throws an exception, your app is in bigger trouble already.
u
yea true, I dont catch exceptions when reading or write from database, app would just crash, but its also comforting the whole user data would not be there, so user would just re-login
partial would have to sync themselves hopefully over time