Abstract #
KDataStore
is my local persistent Android storage solution based on Jetpack’s DataStore.
The emphasis on optimizations is as follows:
- Singleton mode.
- Generating
key
through delegation. - Adapting
MutableStateFlow
for real-time observation, synchronous reading and writing with memory, and asynchronous writing to disk. - Backing up data to handle exceptions.
- Supports extensive types, e.g.
Kotlin Serializable
,Customized
,Nullable
.
See the GitHub repository (includes demo) for more information.
Java
support is for the initial promotion and not considered in my other works.
This page will be moved toMultiplatform
afterIOS
support.
Competitor comparison #
SharedPreferences | MMKV | DataStore | KDataStore | |
---|---|---|---|---|
Performance | Startup: 2.5ms Reading: negligible time consumption. Writing with commit : blocking for 2.3msWriting time consumption with apply is negligible, but it is unsure if the data has been successfully written to disk asynchronously. | Startup: 2.3ms Reading and writing: negligible time consumption | All are performed asynchronously, so only response time consumption is measured: 8.6ms | Startup: 13.5ms This impact is not significant when the file size increases substantially, and it can be also resolved by calling the KDataStore subclass asynchronously in the Application .Reading and writing time consumption is negligible. |
Type safety | No | No | Yes | Yes |
Support for types other than common basic types, String , and Set<String> | Parcelable | Customizable However, it needs to be placed in an independent DataStore | KtSerializable (including common storage types)Customizable | |
Exception during reading | Returns a empty HashMap , which means returning defaults for all | Manual catch | Gets from backup file | |
IOException during writing | Replaces with the backup file without the data, and do not write again. Returns false if via commit | Only catch without handling. | Records at once. Update from backup file at next startup | |
Multiprocess | Manual encapsulation | Supported | In 1.1.0-alpha stage | After DataStore 1.1.0 releases |
Multiplatform | Not supported | Supported | ||
Crypto | Manual encapsulation | Supported | Manual encapsulation | You need to choose a crypto protocol and customize a cipher . |
Additional advantages | Data updated before ANR will not be lost | Small size, jar on Android side is only 12.6 kb | ||
Additional disadvantages | After a power outage or system crash, a lot of data is likely to be lost | Quite new Modeling requires Kotlin. Java is only supported on caller side. |
The test results above are based on 30 sets of String data of Meizu 18s. Source code is in KDataStore.benchmark.
There are serious errors in most storage scheme comparison analysises from other information sources. The official website is relatively accurate, but very one-sided. If you want to explore further, I recommend you test it yourself and view the source code for deep exploration.
Basic Usage #
Take the example of switching darkTheme
with the stored Boolean
.
Model #
Set an independent Android
module, commonly named settings
.
All namings in the following documents refer to settings
.
About KDSFlow
actual KDSFlow
on Android
Call #
Call in other modules
- Observe
Flow
/LiveData
inActivity
/BasicActivity
and bind thetheme
. - Checked
RadioButton
will change with the user’s click. Just set the initial state according toisDarkMode.value
rather than bind. - Update the stored value in the
RadioGroup
listener.
collectOnResume is recommended to observeFlow
inFragment
, of which thesetup
is included in the following.
- Observe
LiveData
inActivity
/BasicActivity
and bind thetheme
. - Checked
RadioButton
will change with the user’s click. Just set the initial state according toisDarkMode.value
rather than bind. - Update the stored value in the
RadioGroup
listener.
Bind state
and theme
Update stored value at RadioButton
Setup #
Configure build.gradle
/build.gradle.kts
as below, or reference the demo in Github
which uses version catalog.
Root Directory #
plugins{
...
id 'org.jetbrains.kotlin.plugin.serialization' version "$version_kt" apply false
}
plugins{
...
id ("org.jetbrains.kotlin.plugin.serialization") version "$version_kt" apply false
}
Model module #
plugins {
...
id 'kotlinx-serialization'
}
dependencies {
...
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1'
implementation 'io.github.shawxingkwok:kt-util:1.0.2'
implementation 'io.github.shawxingkwok:kdatastore:1.0.0'
}
plugins {
...
id ("kotlinx-serialization")
}
dependencies {
...
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation ("io.github.shawxingkwok:kt-util:1.0.2")
implementation ("io.github.shawxingkwok:kdatastore:1.0.0")
}
Caller Side #
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach{
kotlinOptions.freeCompilerArgs += "-Xcontext-receivers"
}
dependencies {
...
implementation 'io.github.shawxingkwok:android-util-view:1.0.8'
implementation 'io.github.shawxingkwok:kdatastore:1.0.0'
implementation project(':modelModuleName') // or remote library
}
dependencies{
...
implementation 'io.github.shawxingkwok:kdatastore:1.0.0'
implementation project(':modelModuleName') // or remote library
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach{
kotlinOptions.freeCompilerArgs += "-Xcontext-receivers"
}
dependencies {
...
implementation ("io.github.shawxingkwok:kdatastore:1.0.0")
implementation ("io.github.shawxingkwok:android-util-view:1.0.8")
implementation (project(":modelModuleName")) // or remote library
}
dependencies{
...
implementation ("io.github.shawxingkwok:kdatastore:1.0.0")
implementation (project(":modelModuleName")) // or remote library
}
IncludeKDataStoreInitializer::class.java
independencies
, in case this caller module introduced startup-runtime.
Type Support #
kotlinx.serialization
usage is similar to Java Serializable
, but multiplatform and more than twice as fast.
Classes annotated with Serializable
, basic types, enum
, Pair
, IntArray
, List
’s default implementation and some others
can be considered as Serializable
.
- When
non-null
, a default value must be declared. - When
nullable
, the default value is restricted tonull
.
Edit in a stored object would not trigger the disk update.
Migration #
Migrate from other storage repositories with this format (judge existence -> migrate -> delete) in which appContext
is from KDataStore
.
Take the example of migrating from SharedPreferences
.
Additionally, there are two built-in functions, delete
and exist
, that assist in migrating from KDataStore
to other places.
Optional arguments #
For the cipher
part, you need to choose a crypto protocol from Java
standard library,
or other libraries to customize it.
Data isolation was introduced in Android API 29, making it relatively safe without crypto which approximately doubles the startup time.
Reset #
All #
Partial #
Take the example of isDarkMode
.
Settings.isDarkMode.reset()
Settings.isDarkMode().reset();