Android Tutorial KeyValueStore class - Simple and efficient key/value data store

Status
Not open for further replies.
KeyValueStore v2 is available here: https://www.b4x.com/android/forum/threads/b4x-keyvaluestore-2-simple-powerful-local-datastore.63633/

In many cases applications need to store all kinds of data.

Key / value data stores (sometimes referred as NoSQL) can offer an alternative to relational databases (SQL). The key / value store offers a simple functionality. It allows you to store all kinds of values, where each value is mapped to a key. Very similar to Maps (as well as Dictionary, Hashtable, HashMap...). The main difference is that the store is persisted in the file system.

KeyValueStore class uses an SQLite database to store and retrieve all kinds of values.

It uses RandomAccessFile.WriteObject or WriteEncryptedObject to save collections and user types.

Using KeyValueStore is similar to using a Map:
B4X:
Sub Process_Globals
   Private kvs As KeyValueStore
End Sub

Sub Activity_Create(FirstTime As Boolean)
   If FirstTime Then
      kvs.Initialize(File.DirDefaultExternal, "datastore")
   End If
   'put a "simple" value
   kvs.PutSimple("time", DateTime.Now)
   'fetch this value
   Log(DateTime.Time(kvs.GetSimple("time")))

   'put a Bitmap
   kvs.PutBitmap("bitmap1", LoadBitmap(File.DirAssets, "asteroids.png"))
   'fetch a bitmap
   Activity.SetBackgroundImage(kvs.GetBitmap("bitmap1"))

   'remove the bitmap from the store
   kvs.Remove("bitmap1")

   'add a collection
   Dim list1 As List
   list1.Initialize
   For i = 1 To 10
      list1.Add("Item #" & i)
   Next
   kvs.PutObject("list1", list1)

   'fetch the collection
   Dim list2 As List = kvs.GetObject("list1")
   Log(list2)
   'encrypt the list
   kvs.PutEncyptedObject("encrypted list", list1, "topsecret")
   Try
      'note that if you run this example in Debug then it will break on this call. Press F5 to continue...
      list2 = kvs.GetEncryptedObject("encrypted list", "wrong password")
   Catch
      Log("Wrong password!")
   End Try
   list2 = kvs.GetEncryptedObject("encrypted list", "topsecret")
   Log(list2)
End Sub

The public methods of KeyValueStore:
B4X:
'Puts a simple value in the store.
'Strings and number types are considered "simple" values.
Sub PutSimple(Key As String, Value As Object) As Boolean

'Puts an object in the store. This method uses RandomAccessFile.WriteObject to save the object in the store.
'It is capable of writing the following types of objects: Lists, Arrays, Maps, Strings, primitive types and user defined types.
'Combinations of these types are also supported. For example, a Map with several lists of arrays can be written.
'The element type inside a collection must be a String OR primitive Type.
Sub PutObject(Key As String, Value As Object) As Boolean

'Similar to PutObject. Encrypts the object before writing it. Note that you can use it to store "simple" types as well.
Sub PutEncyptedObject(Key As String, Value As Object, Password As String) As Boolean

'Puts a bitmap in the store.
Sub PutBitmap(Key As String, Value As Bitmap) As Boolean

'Reads the data from the input stream and saves it in the store.
Sub PutInputStream(Key As String, Value As InputStream) As Boolean

'Removes the key and value mapped to this key.
Sub Remove(Key As String)

'Returns a list with all the keys.
Sub ListKeys As List

'Tests whether a key is available in the store.
Sub ContainsKey(Key As String) As Boolean

'Deletes all data from the store.
Sub DeleteAll

'Returns a "simple" value. See PutSimple.
Sub GetSimple(Key As String) As String

'Returns an InputStream from the store. See PutInputStream.
Sub GetInputStream(Key As String) As InputStream

'Returns a bitmap from the store. See PutBitmap.
Sub GetBitmap(Key As String) As Bitmap

'Returns an object from the store. See PutObject.
Sub GetObject(Key As String) As Object

'Returns an encrypted object from the store. See PutEncryptedObject.
Sub GetEncryptedObject(Key As String, Password As String) As Object

'Closes the store.
Sub Close

So if you do not need the more advanced features of a relational database then KeyValueStore is your probably best solution for data persisting.

The class is included in the attached example. It depends on the SQL and RandomAccessFile libraries.

V1.01 - Fixes an issue with open cursors.
 

Attachments

  • KeyValueStore.zip
    10.7 KB · Views: 4,648
Last edited:

bluedude

Well-Known Member
Licensed User
Longtime User
Erel,

I have an app. with 5-6 activities which all use a database. I declare the keyvaluestore in a code module to make sure it is available to all activities. However, I noticed I get errors when I don't initialize the keyvaluestore again in every activity.

This happens when android destroys my activity.

In code module
Sub prepareStorage
If kvs.IsInitialized =False Then
kvs.Initialize(File.DirDefaultExternal,"datastore")
Else
End If
'storage of variants
kvs.createTable ("test")
end sub

I just call this when main activity is loaded for the first time.

I already noticed I can solve the problem when calling it in every activity by using firsttime, not sure If I want that.
 

cbal03

Member
Licensed User
Longtime User
why not do it in main, activity_create, if(firsttime)...?

Then reference it by main.kvs?
 

Peter van Hoof

Member
Licensed User
Longtime User
Doubles stored to the KeyValueStore using PutSimple are rounded after 6 decimals after the period even if you first store them in a string. this is a problem in the app i am developing at the moment that needs to store GPS coordinates.

I can store a double by adding a character to the start of the string and stripping it off after retrieving it but is there a more elegant way to do this?
 

JoshyDEWstive

Member
Licensed User
Longtime User
Hi Erel,
I really want to use this,
Ive downloaded it now where do i put it?;
Ive tried librarys and nope.

Thanks
 

Peter van Hoof

Member
Licensed User
Longtime User
Hi Erel,
I really want to use this,
Ive downloaded it now where do i put it?;
Ive tried librarys and nope.

Thanks

A good place to put this module would be the shared modules directory directory defined in . [tools] - [configure paths] - [shared modules] so you wont have copy's of it in all your project directory's. then as LucaMs said add it to the project with [Menu Project] - [Add Existing Module]
 

DonManfred

Expert
Licensed User
Longtime User
B4X:
    Dim m As Map
    m.Initialize
    m.Put("x",42)
    kvs.PutObject("map",m)
   
    Dim m As Map
    m.Initialize
    m = kvs.GetObject("map")
 

freetoair

Member
Licensed User
Longtime User
When i start the app in which the AndroidManifest.xml is read only, for skipping the verification of USB, I always get the following message:

"An error has occured in sub:Keyvaluestore_initialize (java line:257)
android.database.sqlite.SQLiteCantOpenDatabaseException:
unknown error (code 14): Could not open database
Continue?"

Otherwise when I install an APK from B4A functioning normally. But after boot always shows this message. Does it have to do with AndroidManifest.xml file?
 

freetoair

Member
Licensed User
Longtime User
No, in this case I do not get this message, but then constantly have to approve of the USB device.
 

freetoair

Member
Licensed User
Longtime User
Before that, I entered the sequence :

<intent-filter>
<action android: name = "android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</ intent-filter>
<meta-data android: name = "android.hardware.usb.action.USB_DEVICE_ATTACHED"
android: resource = "@ xml / device_filter" />

in manifest editor, and

#Region Service Attributes
#StartAtBoot: True
#End Region

And now APK does not start after boot.
 

freetoair

Member
Licensed User
Longtime User
Unfortunately now the problem is the same. Message:

"An error has occured in sub:Keyvaluestore_initialize (java line:257)
android.database.sqlite.SQLiteCantOpenDatabaseException:
unknown error (code 14): Could not open database
Continue?"

it occurs only at boot sequences. When I start APK through B4A everything is fine.
Any solution ?
 
Status
Not open for further replies.
Top