https://kotlinlang.org logo
#android-architecture
Title
# android-architecture
u

ursus

08/14/2020, 3:56 AM
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

streetsofboston

08/14/2020, 4:22 AM
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

gildor

08/14/2020, 4:51 AM
does it mean its not as durable as a sqlite would
It’s definitely not as durable as sqlite
u

ursus

08/14/2020, 8:37 PM
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

streetsofboston

08/14/2020, 8:40 PM
The chance of your preference data not being committed is very very very small.
u

ursus

08/14/2020, 8:41 PM
but it is there, unlike sqlite, also .. no transactions
s

streetsofboston

08/14/2020, 8:41 PM
Shared Preferences is for user preferences and such, a simple key-value store. It should not be used as a proper db. 🙂
u

ursus

08/14/2020, 8:43 PM
so, youd just rather have one table of one row where the value is json?
I have lot of 1:1 relations
s

streetsofboston

08/14/2020, 8:44 PM
Is it for HTTP caching?
u

ursus

08/14/2020, 8:45 PM
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

streetsofboston

08/14/2020, 8:46 PM
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

ursus

08/14/2020, 8:47 PM
yes it needs persisting
but, its not all a single object, i have "user" key, "profile" key, etc.. so not a real transaction 😟
s

streetsofboston

08/14/2020, 8:49 PM
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

ursus

08/14/2020, 8:50 PM
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

streetsofboston

08/14/2020, 8:51 PM
You have multiple users supported by your app?
u

ursus

08/14/2020, 8:53 PM
no just one, data is not deep but very wide
also its a multi module multiapp monorepo
s

streetsofboston

08/14/2020, 8:55 PM
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

ursus

08/14/2020, 9:03 PM
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

streetsofboston

08/14/2020, 9:07 PM
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

ursus

08/14/2020, 9:08 PM
oh so if I use the same editor object it will act as a atomic transaction?
s

streetsofboston

08/14/2020, 9:09 PM
Yup. It is not db-grade guarantee (no rollback if something goes truly wrong, some odd exception, veeeery small chance), but good enough.
u

ursus

08/14/2020, 9:10 PM
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

streetsofboston

08/14/2020, 9:12 PM
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

ursus

08/14/2020, 9:14 PM
Idk, some..reason, if it doesn do that, I dont need transactions, dont it?
s

streetsofboston

08/14/2020, 9:14 PM
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

ursus

08/14/2020, 9:16 PM
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

streetsofboston

08/14/2020, 9:18 PM
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

ursus

08/14/2020, 9:19 PM
right, but I presume I cannot use the same editor instance to transaction-save all the stuff at once
s

streetsofboston

08/14/2020, 9:21 PM
No, you are correct. They’d use a different
Editor
object.
u

ursus

08/14/2020, 9:23 PM
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

streetsofboston

08/14/2020, 9:26 PM
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

ursus

08/14/2020, 9:28 PM
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

streetsofboston

08/14/2020, 9:29 PM
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

ursus

08/14/2020, 9:30 PM
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

streetsofboston

08/14/2020, 9:32 PM
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

ursus

08/14/2020, 9:33 PM
yea that could work, but those products in another prefs would not -.-
s

streetsofboston

08/14/2020, 9:33 PM
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

ursus

08/14/2020, 9:38 PM
well, I dont know -.-, is that fear irrational? 😄
s

streetsofboston

08/14/2020, 9:42 PM
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

ursus

08/14/2020, 9:48 PM
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
4 Views