Introduce
In this article we will learn about DataStore, one of the new data storage solutions in the Android Jetpack package and will gradually replace the existing SharePreferences data type.
The current DataStore in this article has just been introduced and is in the alpha stage, so there are still many changes in the future.
DataStore allows storing data in 2 forms: using key-value pairs or self-defined object types with protocol buffer declaration .
DataStore uses a combination of Kotlin Coroutines and Flow to store and read data asynchronously.
Depending on the purpose of data storage, DataStore will also provide different installation methods: Preferences DataStore and Proto DataStore
- DataStore Preferences : store and retrieve data by keys. This implementation method does not require predefined schemas and it does not guarantee the correctness of the data type to be passed, which must be determined by the programmer.
- Proto DataStore : store Primitive data or self-defined data. This implementation method requires defining a schema in advance using protocal buffers , but it ensures the correctness of the data.
Comparison between SharedPreferences and DataStore
Below is a detailed statistics table between the traditional storage of SharedPreferences and the two new ways to store data. Preferences DataStore and Proto DataStore
We can see the biggest difference that SharedPreferences uses synchronous storage mechanism, while DataStore is based on asynchronous mechanism and does not occupy the UI thread, so it is safe to call without causing affects the main process of the application.
Use the DataStore Preferences to store data
In this article we will try to use Preferences DataStore to read and write a UUID string, displaying a Toast every time we run into the app indicating the value of the UUID string we read from the DataStore Preferences.
Install the library
To use the DataStore Preferences we add the following library to the Gradle file of the project
1 2 3 4 5 | dependencies <span class="token punctuation">{</span> <span class="token comment">// Preferences DataStore</span> implementation <span class="token string">"androidx.datastore:datastore-preferences:1.0.0-alpha01"</span> <span class="token punctuation">}</span> |
In addition, because based on the working mechanism of Coroutines and Flow , we also need to add the following library
1 2 3 4 5 | <span class="token comment">// coroutines</span> implementation <span class="token string">'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'</span> implementation <span class="token string">'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7'</span> implementation <span class="token string">"org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.3.7"</span> |
Install the source code
Create a Preferences DataStore object
Use the Context.createDataStore () method to create an instance of Preferences DataStore. This method takes a parameter name that represents the name of the DataStore Preferences. In the source code, we will declare a global variable dataStore and the constructor will be called in the onCreate () method as follows:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token comment">// declare value for working with datastore</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> dataStore <span class="token operator">:</span> DataStore <span class="token operator"><</span> Preferences <span class="token operator">></span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token operator">:</span> Bundle <span class="token operator">?</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">onCreate</span> <span class="token punctuation">(</span> savedInstanceState <span class="token punctuation">)</span> <span class="token function">setContentView</span> <span class="token punctuation">(</span> R <span class="token punctuation">.</span> layout <span class="token punctuation">.</span> activity_main <span class="token punctuation">)</span> <span class="token comment">//create preference data store</span> dataStore <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">createDataStore</span> <span class="token punctuation">(</span> name <span class="token operator">=</span> <span class="token string">"MyPreferenceDataStore"</span> <span class="token punctuation">)</span> |
Write data into the DataStore Preferences
Since this is a mechanism to store data in key-value form , we need to create the key along with the type of data we want to store first. In this example, we want to store a string UUID with the data type String, so we will declare a variable with the following structure:
1 2 3 | <span class="token comment">//create key for save data type</span> <span class="token keyword">val</span> PREF_UUID <span class="token operator">=</span> preferencesKey <span class="token operator"><</span> String <span class="token operator">></span> <span class="token punctuation">(</span> name <span class="token operator">=</span> <span class="token string">"uuid"</span> <span class="token punctuation">)</span> |
Next, we use the edit () method that the DataStore Preferences provides to write data. Also note that DataStore uses asynchronous storage mechanism in conjunction with Coroutines, so when declaring a function to store data, there will be more suspend at the beginning of the function, as we can see below:
1 2 3 4 5 6 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">saveUUID</span> <span class="token punctuation">(</span> uuid <span class="token operator">:</span> String <span class="token punctuation">)</span> <span class="token punctuation">{</span> dataStore <span class="token punctuation">.</span> <span class="token function">edit</span> <span class="token punctuation">{</span> it <span class="token punctuation">[</span> PREF_UUID <span class="token punctuation">]</span> <span class="token operator">=</span> uuid <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Read recorded data from the DataStore Preferences
Preferences DataStore.data DataStore provides methods to retrieve the data and the data is stored as a Flow. The example here is the string UUID, the output data will be Flow <String> . The function to read data would look like this:
1 2 3 4 5 6 7 8 9 | <span class="token comment">// declare value for receive data</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> uuid <span class="token operator">:</span> Flow <span class="token operator"><</span> String <span class="token operator">></span> <span class="token keyword">fun</span> <span class="token function">readUUID</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> uuid <span class="token operator">=</span> dataStore <span class="token punctuation">.</span> data <span class="token punctuation">.</span> <span class="token function">map</span> <span class="token punctuation">{</span> it <span class="token punctuation">[</span> PREF_UUID <span class="token punctuation">]</span> <span class="token operator">?:</span> <span class="token string">""</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
At this point, we just read the data to get from the DataStore and store it in a uuid variable of type Flow <String> , to be able to use this data we need to use the collect method to listen to the emitted data. from this Flow <String> . It can be understood that this data type is a replacement for RxJava with similar mechanism Observable and Observer , the way to receive data can be seen as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token function">CoroutineScope</span> <span class="token punctuation">(</span> Dispatchers <span class="token punctuation">.</span> Main <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">launch</span> <span class="token punctuation">{</span> uuid <span class="token punctuation">.</span> <span class="token function">collect</span> <span class="token punctuation">{</span> value <span class="token operator">-></span> <span class="token keyword">if</span> <span class="token punctuation">(</span> value <span class="token punctuation">.</span> <span class="token function">isEmpty</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> newUUID <span class="token operator">=</span> UUID <span class="token punctuation">.</span> <span class="token function">randomUUID</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toString</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">showToast</span> <span class="token punctuation">(</span> <span class="token string">"Not have UUID saved. Create new UUID <span class="token interpolation"><span class="token delimiter variable">${</span> newUUID <span class="token delimiter variable">}</span></span> and save to data store"</span> <span class="token punctuation">)</span> <span class="token comment">//delay(5000)</span> <span class="token function">saveUUID</span> <span class="token punctuation">(</span> newUUID <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">showToast</span> <span class="token punctuation">(</span> <span class="token string">"Read UUID from preference data store <span class="token interpolation"><span class="token delimiter variable">${</span> value <span class="token delimiter variable">}</span></span> "</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">showToast</span> <span class="token punctuation">(</span> s <span class="token operator">:</span> String <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">runOnUiThread</span> <span class="token punctuation">{</span> <span class="token keyword">val</span> toast <span class="token operator">=</span> Toast <span class="token punctuation">.</span> <span class="token function">makeText</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">,</span> s <span class="token punctuation">,</span> Toast <span class="token punctuation">.</span> LENGTH_LONG <span class="token punctuation">)</span> toast <span class="token punctuation">.</span> <span class="token function">setGravity</span> <span class="token punctuation">(</span> Gravity <span class="token punctuation">.</span> CENTER <span class="token punctuation">,</span> <span class="token number">0</span> <span class="token punctuation">,</span> <span class="token number">0</span> <span class="token punctuation">)</span> toast <span class="token punctuation">.</span> <span class="token function">show</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The uuid.collect method is a suspend function, so it needs to be run from a Coroutine Scope and listen for data changes, each time there is a change it will automatically listen and update the value like LiveData we usually know. . Here, if the uuid has not been created it will generate a random value and save it to the previously declared DataStore Preferences via the saveUUID function, if it is, it will print the Toast in the middle of the uuid value screen. The results can be seen in the 2 pictures below
At this point we have finished reading and writing a value using the Preferences DataStore storage method. In the next part, we will learn to read and write data using Proto DataStore.
The source code of the project can be found here: https://github.com/hungan1409/ExampleDataStore.git
Refer
https://developer.android.com/topic/libraries/architecture/datastore