B4J Library [B4X] KeyValueStore 2 - Simple & Powerful Local Datastore

Status
Not open for further replies.

Erel

Administrator
Staff member
Licensed User
KeyValueStore is a persistent key/value based data store. It is similar to the useful Map collection, however unlike Map which stores the data in memory, KVS stores the data in a database. This means that you don't need to worry about losing data or saving the state when the program ends.

This version replaces the older version. It is not backwards compatible. You cannot use it with databases created with the previous version.

The main differences between v2 and v1:
The new version is based on B4XSerializator to serialize the values and on B4XCipher to encrypt it.
This means that the data can be shared between B4A, B4J and B4i. For example you can create the data store in B4J and embed it in your mobile app.
B4XSerializator is also faster and simpler to use.

Using KVS is similar to using a Map. You initialize it once (use the Starter service in B4A) and then you can put or get items with Put, Get or GetDefault methods.
You can use PutEncrypted to encrypt the value before it is stored. Use GetEncrypted to get an encrypted value.
If you want to put bitmaps then use PutBitmap and GetBitmap.

The supported types of objects are:

Lists, Maps, Strings, primitives (numbers), user defined types and arrays (only arrays of bytes and arrays of objects are supported).
Custom types should be declared in the main module.
Including combinations of these types (a list that holds maps for example).

KeyValueStore depends on the following libraries: SQL, RandomAccessFile and B4XEncryption (iEncryption on B4i).
Note that in B4J you need to download the bouncy castle jar and add the following two lines to the main module:
B4X:
#AdditionalJar: sqlite-jdbc-3.7.2
#AdditionalJar: bcprov-jdk15on-154

A B4A example is attached. The class module is compatible with B4A, B4J and B4i.

Tip: Check CloudKVS for an auto-synchronizing solution: https://www.b4x.com/android/forum/threads/b4x-cloudkvs-synchronized-key-value-store.63536/#content

Updates:

- V2.20 - Adds support for GetMapAsync and PutMapAsync. These methods allow to asynchronously insert or retrieve multiple items.
Examples:
B4X:
'getting all items:
Wait For (Starter.kvs.GetMapAsync(Starter.kvs.ListKeys)) Complete (Result As Map)
Log(Result)
'getting specific items:
Wait For (Starter.kvs.GetMapAsync(Array("Key1", "Key2", "Key3")) Complete (Result As Map)
Log(Result)
Note that starting from B4A v8.0 KeyValueStore2 is included as an internal library.
 

Attachments

Last edited:

stevel05

Expert
Licensed User
Brilliant, thanks Erel. I've just been looking at this. It makes it simple to store all manner of appdata without having to create multiple map saves to the appdata folder.

Also, if you don't want to use encryption, comment out the two Encryption subs (or better still use conditional compilation) and there is no need to load the additional bouncy castle jar.

I particularly like the basic get and put methods, which are the same as using a standard map.
 

postasat

Member
Licensed User
Hi,

is it mandatory to use the Starter service to initialize kvs ?
I need to record a long series of list (with different key), how can I do with the new code ?

Thank you.
 

Mahares

Well Known Member
Licensed User
Until @Erel gets back with you Sunday, i am going to take a stab at this and your next question:
is it mandatory to use the Starter service to initialize kvs ?
I do not think it is essential that you use the starter service. You can declare and initialize the kvs in any module.

I need to record a long series of list (with different key), how can I do with the new code ?
I hope I understood your question, but here is my interpretation code wise:
B4X:
Sub Activity_Create(FirstTime As Boolean)
   
    Starter.kvs.DeleteAll   'start with a new datastore.
   
    Dim list2 As List
    list2.Initialize
    list2.AddAll(Array As String("USA","CHINA","ANGOLA","FINLAND"))
    Starter.kvs.Put("lstCountry", list2)
   
    list2.Clear
    list2.AddAll(Array As String("AMERICA","ASIA","AFRICA","EUROPE"))
    Starter.kvs.Put("lstContinent", list2)
   
    list2.Clear
    list2.AddAll(Array As String("WASHINGTON","BEIJING","LUANDA","HELSINKI"))
    Starter.kvs.Put("lstCapital", list2)


    Dim MyList As List =  Starter.kvs.Get("lstCapital")
    For i=0 To MyList.Size -1
        Log(MyList.Get(i))  'displays all capitals in a list
    Next

End Sub
 

postasat

Member
Licensed User
Hi,
thank you for your reply.

I'm using a timer to store bluetooth data, every second, in a kvs file.
The data are in a list.
The code I use to store the data every second is:
B4X:
Sub memorizza_dati
'Record data
   
    If recordsequence > 0 Then
        list1.Add("Item #" & recordsequence) '0
        list1.Add(DateTime.time(DateTime.Now)) '1 orario (oo:mm:ss)
        list1.Add(brm(1))         
        list1.Add("0") ' spare
        list1.Add("0") ' spare
        list1.Add("0") ' spare
       
        kvs1.PutObject("Item #" & recordsequence, list1)
        list1.Clear
    Else
        kvs1.Initialize(File.DirRootExternal & "/Download/Hearts/Lezioni/" & Main.nome_classe,Main.nome_nuova_lezione & ".les")
        list1.Initialize
    End If   
       
    recordsequence = recordsequence + 1

End Sub
To convert code from kvs v1 to kvs v2 I must only change .getobject and .putobject to .get e .put ?
 

asales

Well-Known Member
Licensed User
This version replaces the older version. It is not backwards compatible. You cannot use it with databases created with the previous version.
I can change the name of this class to KeyValueStore2.bas and uses together with KeyValueStore version 1?
Like this?:
B4X:
Public kvs As KeyValueStore     'version 1

Public kvs2 As KeyValueStore2     'version 2
 

asales

Well-Known Member
Licensed User
No. KVS2 and KVS1 are NOT compatible.
Yes, but I want to use the 2 versions with differents files.
The old file with KVS1 (to mantain compatibility in my app) and the new file to KVS2, with the new options that I will put in my app.
It's only rename this class to KeyValueStore2.bas to make this?
 

Mahares

Well Known Member
Licensed User
is it possible to convert old kvs created datastore files to new kvs2 ?
Here is how I did it to convert a version1 datastore ta a version2 datastore:
Create a project with both classes. Then I used below code:

B4X:
Sub Process_Globals
    Private kvs As KeyValueStore   'version 1
    Private kvs2 As KeyValueStoreTwo  'version 2
End Sub
Sub Activity_Create(FirstTime As Boolean)
    'Store items in datastore1
    If FirstTime Then
        kvs.Initialize(File.DirRootExternal, "datastore1")
    End If
    kvs.PutSimple("time", DateTime.time(DateTime.Now) )
    kvs.PutBitmap("bitmap1", LoadBitmap(File.DirAssets, "smiley.gif"))  'or whatever file in assetts
    Dim list1 As List
    list1.Initialize
    For i = 1 To 10
        list1.Add("Item #" & i)
    Next
    kvs.PutObject("list1", list1)
 
    'Store items in datastore2
    If FirstTime Then
        kvs2.Initialize(File.DirRootExternal, "datastore2")
    End If
    kvs2.Put("time",  kvs.GetSimple("time"))
    kvs2.PutBitmap("bitmap1", kvs.GetBitmap("bitmap1"))
    kvs2.Put("list1",kvs.GetObject("list1"))

    'Delete datastore1
    kvs.Close
    File.Delete(File.DirRootExternal,"datastore1")
 
    'Retrieve items stored in datastore2
    Log(kvs2.Get("time"))
    Activity.SetBackgroundImage(kvs2.GetBitmap("bitmap1"))
    Dim MyList As List =  kvs2.Get("list1")
    For i=0 To MyList.Size -1
        Log("Items in list: " & MyList.Get(i))  'displays all items in the list
    Next
     
    For i=0 To kvs2.ListKeys.Size-1
        Log("keys are: " & kvs2.ListKeys.Get(i) )
    Next 
End Sub
If everyone thinks it is the proper way to convert a version1 to version2 store, I can create a code snippet for this. Awaiting feedback.
 

welu1805

Active Member
Licensed User
Hi Erel,

I wanted to use the new class in my first B4J project with encrypted values, but I get an error:

B4X:
Sub GetSettings
  transferDir = kvs.GetEncrypted("K1", KeyPW)
End Sub

Sub PutSettings
  kvs.PutEncrypted("K1", KeyPW, transferDir)
End Sub
Program started.
Error occurred on line: 56 (KeyValueStore)
org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted
at org.bouncycastle.crypto.paddings.PKCS7Padding.padCount(Unknown Source)
at org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher.doFinal(Unknown Source)
at anywheresoftware.b4x.object.B4XEncryption.Decrypt(B4XEncryption.java:73)
at de.wedersoft.MusicBookTransfer.keyvaluestore._getencrypted(keyvaluestore.java:80)
at de.wedersoft.MusicBookTransfer.main._getsettings(main.java:130)
at de.wedersoft.MusicBookTransfer.main._appstart(main.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:607)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:228)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:158)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:93)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:90)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:84)
at de.wedersoft.MusicBookTransfer.main.start(main.java:36)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$163(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$176(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$174(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$175(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$149(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)

Can you help?
Lutz
 

Anser

Well-Known Member
Licensed User
Hi Erel,

I wanted to use the new class in my first B4J project with encrypted values, but I get an error:

B4X:
Sub GetSettings
  transferDir = kvs.GetEncrypted("K1", KeyPW)
End Sub

Sub PutSettings
  kvs.PutEncrypted("K1", KeyPW, transferDir)
End Sub
I believe that the password should be the past parameter ?:confused:
B4X:
Sub PutSettings
  kvs.PutEncrypted("K1",  transferDir, KeyPW)
End Sub
 

LucaMs

Expert
Licensed User
I tried to save a Map and I got this error message:

java.lang.RuntimeException: Cannot serialize object: android.graphics.Bitmap@21e63b48

The Map contains few string items and one bitmap, for example (not real code):

M as Map
MyBmp as Bitmap

M.Put("Name", "sdfkj")
M.Put("xxx", "zzz")
M.Put("bmp", MyBitmap)


kvs.Put("data", M)
 

Douglas Farias

Expert
Licensed User
I tried to save a Map and I got this error message:

java.lang.RuntimeException: Cannot serialize object: android.graphics.Bitmap@21e63b48

The Map contains few string items and one bitmap, for example (not real code):

M as Map
MyBmp as Bitmap

M.Put("Name", "sdfkj")
M.Put("xxx", "zzz")
M.Put("bmp", MyBitmap)


kvs.Put("data", M)
put i think its only for objetcs
need use kvs.PutBitmap
 
Status
Not open for further replies.
Top