Android Question [Solved]How to save a list of arrays?

RMarra

Member
Licensed User
I have a list and each object in the list is an array of objects. I'm using this to display in a B4XTable (which works fine). I need to store it when I exit the activity, so I can restore it the next time the activity starts. I tried Writelist but it doesn't like the array and (I think) it converts it to a string. I thought about writing it out as a CSV, but I don't see how without writing my own "convert-list-to-csv" routine. I don't really care how it is stored, as long as I can get it back correctly. Is there an easy way to do this?
 

ronell

Well-Known Member
Licensed User
use randomaccessfile library
B4X:
randomaccessfile.writeobject
search the forum for samples
 

RMarra

Member
Licensed User
What is best practices on deciding where to put this file? In your examples you use DirInternal. I've tried this and a few other locations and either I get permission errors or can't find the file. I'd like to put the file on the SD card if possible.
 

RMarra

Member
Licensed User
I can't get randomaccessfile to work for me. I'm only writing 5 rows containing a date, time and an integer. It's trying to create a huge file:

java.lang.OutOfMemoryError: Failed to allocate a 704739496 byte allocation with 6291456 free bytes and 253MB until OOM, max allowed footprint 8418576, growth limit 268435456

I could just write it out as text and parse it myself, but I'm trying to learn how to use new features.

Also, how can I see if there is a giant file orphaned out there?

Any other suggestions on how to do this? I've included the code snippet
 

Attachments

RMarra

Member
Licensed User
I wrote a test program to write out 1 row with B4xReadObject to position 1 and then immediately read it back and it works. I then write out 1 rows to position 2 and then immediately read it back and it works. I then try to reread #1 and it fails with (Exception) java.lang.Exception: java.io.EOFException: Unexpected end of ZLIB input stream.
B4X:
                raf.Initialize(File.DirInternal, "InsulinHistory.dat",  False)
                Try
                    Dim rafRow(3) As String
                                        
                    rafRow(0) = "0"
                    rafRow(1) = "seconds=" & DateTime.GetSecond(DateTime.Now)
                    rafRow(2) = "2"
                    raf.WriteB4XObject(rafRow, 1)
                    rafRow = raf.ReadB4XObject(1)
                    Log(rafRow(1))
                        
                    rafRow(0) = "0"
                    rafRow(1) = "seconds=" & DateTime.GetSecond(DateTime.Now)
                    rafRow(2) = "2"
                    raf.WriteB4XObject(rafRow, 2)
                    rafRow = raf.ReadB4XObject(2)
                    Log(rafRow(1))
                        
                    rafRow = raf.ReadB4XObject(1)
                    Log(rafRow(1))
                Catch
                    Log(LastException)
                End Try
I've working on this for two days. Are there any other ways to do this? XML maybe?
 

emexes

Well-Known Member
Licensed User
I am not set up right now to test this in B4A, but I think the problem might be that the Position parameter of the Read and Write methods is in bytes, not records.

So if your object ended up being saved as eg 120 bytes, then:

raf.WriteB4XObject(rafRow, 1) 'writes 120 bytes to file bytes 1..120
raf.WriteB4XObject(rafRow, 2) 'writes 120 bytes to file bytes 2..121, ie overwriting bytes 2..120 of the previous object

and then:

rafRow = raf.ReadB4XObject(1) 'reads bytes 1 (first byte of first object) and 2 (first byte of second object) and probably then careers off into the weeds

Imagine that the first four bytes are the length of the data to follow (or perhaps an object type identifier). After writing the first object, bytes 1..4 of the file would be 0116. After writing the second object, bytes 1..4 would be 0011 (the first 0 being the start of first object, and the 011 being the start of the second object). ReadB4XObject would likely (and reasonably) fail on reading from file position 1, since the data is incorrect. It should still read ok from file position 2.

The solution is to write the objects sequentially to the file, and likewise when reading them back. For each read/write operation, use:

upload_2019-9-18_9-4-39.png

for the Position parameter, eg:

raf.WriteB4XObject(rafRow, 0) 'writes first object to start of file (ie offset 0 bytes from beginning), at file bytes eg 0..119
...
raf.WriteB4XObject(rafRow, raf.CurrentPosition) 'writes second object to file, at file bytes eg 120..239
...
raf.WriteB4XObject(rafRow, raf.CurrentPosition) 'writes third object object to file, at file bytes eg 240..359

and then likewise for reading them back.

If you want to read them back in some arbitrary order (ie, random / direct access) then you will need to keep track of the CurrentPosition start address of each object. These would usually be kept in an array (properly, of Longs, but Ints will do the job for files < 2 GB, ie most everything except video and large databases). Next time you need this index array, you can either reconstruct it (by sequentially reading the file and noting CurrentPosition prior to each read) or you could save it to the end of the file followed by a WriteLong (or WriteInt?) of the CurrentPosition of that array.
 
Top