Introduce
In Part 1 we looked at and made an example of using Preferences DataStore to read and write a UUID string. Following this section, we will learn about how to use Proto DataStore to read and write a token string. Writing data from Proto DataStore takes more steps and requires pre-defining a schema file in .proto format in the protocol buffer language. If anyone is not familiar with this language, you can find out through the link here https://developers.google.com/protocol-buffers/docs/overview
Use Proto DataStore to store data
Defines the Schema file used for storing information
As introduced in the previous section, Proto DataStore is a way to ensure that the information of the data type is always correct during read and write operations. This guarantee is done by pre-defining a schema file and the program when compiling will read information from the previously defined schema file and generate read and write functions according to the specified data type. that it helps the data to be manipulated correctly.
This proto file is specified to be stored in the app / src / main / proto directory if your project does not have one, then create the correct path as above. Next we proceed to create a file according to the structure of the protobuf language. Here the example named a file MyProtoDataStore.proto has the following information:
1 2 3 4 5 6 7 8 9 | syntax = "proto3"; option java_package = "com.example.datastore"; option java_multiple_files = true; message TestModel { string token = 1; } |
The meaning of this file is to create a class named TestModel with the package defined as “com.example.datastore” . In this class, the first element is a variable named token with the data type string , the number 1 represents the first position, not the default value of the token variable. We can see the compile file when running the program will generate the following form.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <span class="token comment">// Generated by the protocol buffer compiler. DO NOT EDIT!</span> <span class="token comment">// source: MyProtoDataStore.proto</span> <span class="token keyword">package</span> <span class="token namespace">com <span class="token punctuation">.</span> example <span class="token punctuation">.</span> datastore</span> <span class="token punctuation">;</span> <span class="token comment">/** * Protobuf type {@code TestModel} */</span> <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">TestModel</span> <span class="token keyword">extends</span> com <span class="token punctuation">.</span> google <span class="token punctuation">.</span> protobuf <span class="token punctuation">.</span> <span class="token class-name">GeneratedMessageLite</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">TestModel</span> <span class="token punctuation">,</span> <span class="token class-name">TestModel</span> <span class="token punctuation">.</span> <span class="token class-name">Builder</span> <span class="token punctuation">></span></span> <span class="token keyword">implements</span> <span class="token comment">// @@protoc_insertion_point(message_implements:TestModel)</span> <span class="token class-name">TestModelOrBuilder</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">TestModel</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> token_ <span class="token operator">=</span> <span class="token string">""</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> TOKEN_FIELD_NUMBER <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">;</span> <span class="token keyword">private</span> java <span class="token punctuation">.</span> lang <span class="token punctuation">.</span> <span class="token class-name">String</span> token_ <span class="token punctuation">;</span> <span class="token comment">/** * <code>string token = 1;</code> * @return The token. */</span> <span class="token annotation punctuation">@java</span> <span class="token punctuation">.</span> lang <span class="token punctuation">.</span> <span class="token class-name">Override</span> <span class="token keyword">public</span> java <span class="token punctuation">.</span> lang <span class="token punctuation">.</span> <span class="token class-name">String</span> <span class="token function">getToken</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> token_ <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * <code>string token = 1;</code> * @param value The token to set. */</span> <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">setToken</span> <span class="token punctuation">(</span> java <span class="token punctuation">.</span> lang <span class="token punctuation">.</span> <span class="token class-name">String</span> value <span class="token punctuation">)</span> <span class="token punctuation">{</span> value <span class="token punctuation">.</span> <span class="token function">getClass</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> token_ <span class="token operator">=</span> value <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * <code>string token = 1;</code> */</span> <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">clearToken</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> token_ <span class="token operator">=</span> <span class="token function">getDefaultInstance</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getToken</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> |
Install the library
To use Proto DataStore we need to add the following library to the Gradle file of the project:
- The first is the plugins of the protobuf library
1 2 3 4 5 | plugins <span class="token punctuation">{</span> <span class="token operator">..</span> <span class="token punctuation">.</span> id <span class="token string">"com.google.protobuf"</span> version <span class="token string">"0.8.12"</span> <span class="token punctuation">}</span> |
- Next are the libraries to use proto datastore in the project
1 2 3 4 5 6 7 8 9 10 11 12 13 | dependencies { ... // Proto DataStore implementation "androidx.datastore:datastore-core:1.0.0-alpha01" implementation "com.google.protobuf:protobuf-javalite:3.11.0" // coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.3.7" } |
- Finally is the specification to help convert the schema file defined earlier in app / src / main / proto to the corresponding java file that can be used and called from within the program’s source code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | protobuf <span class="token punctuation">{</span> protoc <span class="token punctuation">{</span> artifact <span class="token operator">=</span> <span class="token string">"com.google.protobuf:protoc:3.11.0"</span> <span class="token punctuation">}</span> <span class="token comment">// Generates the java Protobuf-lite code for the Protobufs in this project. See</span> <span class="token comment">// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation</span> <span class="token comment">// for more information.</span> generateProtoTasks <span class="token punctuation">{</span> <span class="token function">all</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">each</span> <span class="token punctuation">{</span> task <span class="token operator">-></span> task <span class="token punctuation">.</span> <span class="token function">builtins</span> <span class="token punctuation">{</span> java <span class="token punctuation">{</span> option <span class="token string">'lite'</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Create a Proto DataStore object
There are two things to do to create a Proto Datastore object for storing the type of object that we defined from the .proto file earlier:
- Define a class that will implement Serializer <T> , where T is the data type we defined. This Serializer class will tell the DataStore how to read and write our data we defined. The content looks like the class MyProtoDataStoreSerializer below:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">object</span> MyProtoDataStoreSerializer <span class="token operator">:</span> Serializer <span class="token operator"><</span> TestModel <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">readFrom</span> <span class="token punctuation">(</span> input <span class="token operator">:</span> InputStream <span class="token punctuation">)</span> <span class="token operator">:</span> TestModel <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> TestModel <span class="token punctuation">.</span> <span class="token function">parseFrom</span> <span class="token punctuation">(</span> input <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> exception <span class="token operator">:</span> InvalidProtocolBufferException <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token function">CorruptionException</span> <span class="token punctuation">(</span> <span class="token string">"Cannot read proto."</span> <span class="token punctuation">,</span> exception <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">writeTo</span> <span class="token punctuation">(</span> t <span class="token operator">:</span> TestModel <span class="token punctuation">,</span> output <span class="token operator">:</span> OutputStream <span class="token punctuation">)</span> <span class="token operator">=</span> t <span class="token punctuation">.</span> <span class="token function">writeTo</span> <span class="token punctuation">(</span> output <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
- Next, use the Context.createDataStore () method to create a DataStore <T> object, where T is the defined data type from the proto file. This function will have 2 parameters: fileName and serializer . Where fileName is the filename used for storing the data here is MyProtoDataStore.proto , while the serializer is the parameter that tells DataStore the name of the serializer class that was defined earlier, which is the class MyProtoDataStoreSerializer . The specific code is as below:
1 2 3 4 5 6 7 8 | <span class="token comment">//create proto data store</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> protoDataStore <span class="token operator">:</span> DataStore <span class="token operator"><</span> TestModel <span class="token operator">></span> protoDataStore <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> fileName <span class="token operator">=</span> <span class="token string">"MyProtoDataStore.proto"</span> <span class="token punctuation">,</span> serializer <span class="token operator">=</span> MyProtoDataStoreSerializer <span class="token punctuation">)</span> |
Write data to Proto DataStore
Proto DataStore provides updateData () method to perform an object update operation to DataStore. In this example we want to store a token string, so the logging would be done as follows:
1 2 3 4 5 6 | <span class="token keyword">suspend</span> <span class="token keyword">fun</span> <span class="token function">saveToken</span> <span class="token punctuation">(</span> token <span class="token operator">:</span> String <span class="token punctuation">)</span> <span class="token punctuation">{</span> protoDataStore <span class="token punctuation">.</span> <span class="token function">updateData</span> <span class="token punctuation">{</span> it <span class="token punctuation">.</span> <span class="token function">toBuilder</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setToken</span> <span class="token punctuation">(</span> token <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Note like the DataStore Preferences , Proto DataStore also implements asynchronous storage associated with Coroutines, so when declaring a function to store data, there will be more suspend at the top of the function.
Read recorded data from Proto DataStore
Use the DataStore.data method to retrieve data and the data is stored as a Flow . The example here is the token string, the output data will be in the form Flow <String> . The function to read data would look like this:
1 2 3 4 5 6 7 | <span class="token keyword">lateinit</span> <span class="token keyword">var</span> token <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">readToken</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> token <span class="token operator">=</span> protoDataStore <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> token <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Similar to part 1, we must also use the collect method to listen to the data emitted from this Flow <String> .
1 2 3 4 5 6 7 8 9 10 11 12 13 | <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> token <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> newToken <span class="token operator">=</span> <span class="token string">"Your token ABCDEF"</span> <span class="token function">showToast</span> <span class="token punctuation">(</span> <span class="token string">"Not have token saved. Create new token <span class="token interpolation"><span class="token delimiter variable">${</span> newToken <span class="token delimiter variable">}</span></span> and save to Proto DataStore"</span> <span class="token punctuation">)</span> <span class="token function">saveToken</span> <span class="token punctuation">(</span> newToken <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 the token from proto 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> |
Results when running the program:
At this point, we have finished reading and writing a value using the Proto DataStore storage method.
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