OK, if the the files are already zipped to .apk then I will just have a go at adding all that (unzipped) to the .apk and see how it all goes.Which reminds me: didn't the APK compression (pretty sure it's a Zip file) work as (un)advertised? You might be doing stuff you don't need to do. Although it'll chew up storage on the phone compared to if you decompress on-the-fly whilst reading, so...
Unless you can read directly from File.DirAssets... tbh I don't think I've ever tried that... always copied to File.DirInternal first... I think was because RandomAccessFile operations didn't work with DirAssets files. Maybe sequential reading via a Stream works ok.
OK, if the the files are already zipped to .apk then I will just have a go at adding all that (unzipped) to the .apk and see how it all goes.
I added 14 .csv files, uncompressed 102 Mb to the Assets folder and compiled.Which reminds me: didn't the APK compression (pretty sure it's a Zip file) work as (un)advertised? You might be doing stuff you don't need to do. Although it'll chew up storage on the phone compared to if you decompress on-the-fly whilst reading, so...
Unless you can read directly from File.DirAssets... tbh I don't think I've ever tried that... always copied to File.DirInternal first... I think was because RandomAccessFile operations didn't work with DirAssets files. Maybe sequential reading via a Stream works ok.
Two problems I encountered with this:I added 14 .csv files, uncompressed 102 Mb to the Assets folder and compiled.
.apk size went from 14.2 Mb to 38.7 Mb.
The size of those 14 files, zipped with 7-zip is 11.5 Mb, so it seems 7-zip compresses about twice as much as the .apk zipper.
Still, not sure if it is worth the extra trouble to add a zipped file to the .apk.
Also 38.7 Mb seems OK for the .apk, so I probably go with adding unzipped files.
Happy to be proven wrong here.
RBS
In debug mode I can see where the error occurs:Two problems I encountered with this:
1. Not sure if it is to do with the size of the .apk or the size of the project, but the B4A IDE sometimes goes unresponsive. It recovers OK, but
it is somewhat worrying.
2. Although the .csv are added in the Files tab and do show in the Assets folder I get this error message:
java.lang.RuntimeException: java.io.FileNotFoundException: AssetsDir/postcode_lsoa11cd$1.csv: open failed: ENOENT (No such file or directory)
at anywheresoftware.b4a.keywords.Common$14.run(Common.java:1750)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8751)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
Caused by: java.io.FileNotFoundException: AssetsDir/postcode_lsoa11cd$1.csv: open failed: ENOENT (No such file or directory)
at libcore.io.IoBridge.open(IoBridge.java:575)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:289)
at anywheresoftware.b4a.randomaccessfile.RandomAccessFile.Initialize2(RandomAccessFile.java:103)
at anywheresoftware.b4a.randomaccessfile.RandomAccessFile.Initialize(RandomAccessFile.java:96)
at b4a.exampleljjll.b4xmainpage$ResumableSub_GetCharacterEncodingFromFile.resume(b4xmainpage.java:18949)
at b4a.exampleljjll.b4xmainpage._getcharacterencodingfromfile(b4xmainpage.java:18904)
at b4a.exampleljjll.b4xmainpage$ResumableSub_CSV2SQLiteTable.resume(b4xmainpage.java:13106)
at b4a.exampleljjll.b4xmainpage._csv2sqlitetable(b4xmainpage.java:12818)
at b4a.exampleljjll.b4xmainpage$ResumableSub_PopulateTable.resume(b4xmainpage.java:41600)
at anywheresoftware.b4a.keywords.Common$14.run(Common.java:1748)
... 8 more
Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
at libcore.io.Linux.open(Native Method)
at libcore.io.ForwardingOs.open(ForwardingOs.java:567)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:273)
at libcore.io.ForwardingOs.open(ForwardingOs.java:567)
at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:8611)
at libcore.io.IoBridge.open(IoBridge.java:561)
... 17 more
I do use a custom class to move data from .csv to SQLite and this uses RandomAccessFile.
Could this be the problem?
RBS
With a copy to File.DirInternal all works fine.In debug mode I can see where the error occurs:
Log(tFF.strFolder & " _ " & tFF.strFile)
RAF.Initialize(tFF.strFolder, tFF.strFile, True)
The log shows OK:
AssetsDir _ POSTCODE_LSOA11CD$1.csv
I can see now though that RAF.Initialize can't be used on files in the Asset folder, so maybe I need to copy them or have them
somewhere else eg File.DirInternal?
RBS
I can see now though that RAF.Initialize can't be used on files in the Asset folder
it seems 7-zip compresses about twice as much as the .apk zipper.
finding a file small enough
>find "UPALONG" *psv
---------- GNAF_CORE_RETIRED.PSV
GAVIC414546897|29-04-2004|12A UPALONG RD, MOUNT DANDENONG VIC 3767|||||||12A|||UPALONG|ROAD||MOUNT DANDENONG|VIC|3767|2\PS542209|20648430000|PRINCIPAL||||FRONTAGE CENTRE SETBACK|145.35421241|-37.83442417
GAVIC414546895|29-04-2004|1 UPALONG RD, MOUNT DANDENONG VIC 3767|||||||1|||UPALONG|ROAD||MOUNT DANDENONG|VIC|3767||20648430000|PRINCIPAL||||STREET LOCALITY|145.35499422|-37.83491969
>pkzip25 -add -speed testfast *retired*
>pkzip25 -add -maximum testsmall *retired*
>7z a -mx1 testfast *retired*
>7z a -mx9 testsmall *retired*
>dir /od
17/08/2023 01:52 PM 135,994,541 GNAF_CORE_RETIRED.psv
20/02/2025 12:26 PM 50,067,680 testfast.zip
20/02/2025 12:27 PM 43,284,277 testsmall.zip
20/02/2025 12:31 PM 41,776,507 testfast.7z
20/02/2025 12:33 PM 15,988,445 testsmall.7z
> Although now I'm having flashbacks to you and I working on reading large CSV files and using RandomAccessFileI could well believe that RandomAccessFile doesn't work with files read directly from the assets folder, if those files require decompressing.
Decompression is usually a sequential process, where later data depends on earlier data.
But it might work if you read it sequentially with an InputStream.
Might even be able to use TextReader with it, to read it line-by-line.
Although now I'm having flashbacks to you and I working on reading large CSV files and using RandomAccessFile because of some anomaly with the EOL bytes, or long lines, or... maybe it was a speed issue.
Sub GetCharacterEncodingFromFile(tFF As tFolderAndFile, lLookAtXBytes As Long) As ResumableSub
Dim i As Long
Dim RAF As RandomAccessFile
Dim lBytes As Long
Dim PreviousByte As Int 'actually just the top 2 bits
Dim ThisByte As Int = 0 'actually just the top 2 bits
Dim InvalidUTF8Flag As Boolean = False
'------------------------------------------------
'this can't be used on files in File.DirAssets !!
'------------------------------------------------
RAF.Initialize(tFF.strFolder, tFF.strFile, True)
lBytes = lLookAtXBytes
Dim TextBytes(lBytes) As Byte
RAF.ReadBytes(TextBytes, 0, lBytes, 0)
For i = 0 To TextBytes.Length - 1
PreviousByte = ThisByte
ThisByte = Bit.And(TextBytes(i), 0xC0) 'top 2 bits
If PreviousByte = 0xC0 Then 'if multibyte start
If ThisByte <> 0x80 Then 'not followed by multibyte continuation
InvalidUTF8Flag = True
Exit
End If
else if ThisByte = 0x80 Then 'if multibyte continuation
If PreviousByte < 0x80 Then 'not preceded by multibyte start or continuation
InvalidUTF8Flag = True
Exit
End If
End If
Next
RAF.Close
If InvalidUTF8Flag Then
Return "windows-1252"
Else
Return "UTF-8" 'or plain ASCII
End If
End Sub
But HttpJob isn't downloading the exact same link. Nor coming up with an error.
Sub Button1_Click
'''xui.MsgboxAsync("Hello World!", "B4X")
Dim fdir As String = File.DirApp
Dim fname As String = "GNAF_CORE_RETIRED.psv.gz"
#IF DEBUG
If File.Exists(fdir, fname) Then
File.Delete(fdir, fname) 'force download during testing
End If
#END IF
If Not(File.Exists(fdir, fname)) Then
'https://drive.google.com/file/d/13WGOgiVH3vkrl6RbMSo_xTgbENO5sa_c/view Dim url As String = "https://drive.usercontent.google.com/download?id=13WGOgiVH3vkrl6RbMSo_xTgbENO5sa_c&export=download&authuser=0&confirm=t&uuid=dbd77f3f-99a0-4e8e-a7f2-51dadc7e872a&at=AEz70l6Ng30IuPp8vVw7-RfYxQZ6%3A1740019218647"
Dim url As String = "https://drive.usercontent.google.com/download?id=13WGOgiVH3vkrl6RbMSo_xTgbENO5sa_c&export=download&authuser=0&confirm=t&uuid=dbd77f3f-99a0-4e8e-a7f2-51dadc7e872a&at=AEz70l6Ng30IuPp8vVw7-RfYxQZ6%3A1740019218647"
Dim url As String = "https://drive.usercontent.google.com/download?id=13WGOgiVH3vkrl6RbMSo_xTgbENO5sa_c&export=download&confirm=t"
'''Dim url As String = "https://www.redlightcemetery.com/rbs/" & fname
Dim job As HttpJob
job.Initialize("DownloadJob", Me)
job.Download(url)
Log("Downloading from: " & url)
Dim StartTime As Long = DateTime.Now
Wait For (job) JobDone(job As HttpJob)
Dim EndTime As Long = DateTime.Now
Log(job.Success)
If job.Success Then
' Save the downloaded file
Dim out As OutputStream = File.OpenOutput(fdir, fname, False)
File.Copy2(job.GetInputStream, out)
out.Close
Log("Downloaded " & File.Size(fdir, fname) & " bytes in " & ((EndTime - StartTime + 999) / 1000).As(Int) & " seconds")
Else
Log("Error downloading file: " & job.ErrorMessage)
End If
job.Release
End If
If File.Exists(fdir, fname) Then
Dim in As InputStream = File.OpenInput(fdir, fname)
Dim compress As CompressedStreams
Dim ingzip As InputStream = compress.WrapInputStream(in, "gzip")
Dim tr As TextReader
tr.Initialize(ingzip)
Dim StartTime As Long = DateTime.Now
Dim LineNumber As Int = 0
Do While True
Dim L As String = tr.ReadLine
If L = Null Then Exit
LineNumber = LineNumber + 1
If L.Contains("UPALO") Then
Dim F() As String = Regex.Split("\|", L)
Dim HeaderFlag As Boolean = False
For I = 9 To 17
If I < F.Length Then
If F(I).Length <> 0 Then
If Not(HeaderFlag) Then
Log("Found on line " & LineNumber)
HeaderFlag = True
End If
Log(I & TAB & F(I))
End If
End If
Next
End If
Loop
Dim EndTime As Long = DateTime.Now
Dim ElapsedTime As Int = EndTime - StartTime
Log("Searched " & LineNumber & " lines in " & ElapsedTime & " ms direct from gzip file")
If ElapsedTime <> 0 Then
Log((LineNumber / ElapsedTime * 1000).As(Int) & " lines per second")
End If
tr.Close
ingzip.Close
in.Close
End If
End Sub
Waiting for debugger to connect...
Program started.
Downloading from: https://drive.usercontent.google.com/download?id=13WGOgiVH3vkrl6RbMSo_xTgbENO5sa_c&export=download&confirm=t
true
Downloaded 44409330 bytes in 12 seconds
Found on line 215037
9 12A
12 UPALONG
13 ROAD
15 MOUNT DANDENONG
16 VIC
17 3767
Found on line 216442
9 1
12 UPALONG
13 ROAD
15 MOUNT DANDENONG
16 VIC
17 3767
Searched 720650 lines in 1391 ms direct from gzip file
518080 lines per second
My file size will not reach 100 Mb, so I should be OK.So the upshot of all that is that your original idea looks good, for files that gzip to up to 2 GB, provided that your users have a working internet connection when they first run your app, and are ok with the download bandwidth cost (if any, eg international roaming). Then again, they'd still be doing much the same size download even if it's buried inside the APK, so... I guess it's not really a factor.
The only downside I can see is: will the same gdrive download link still work if the gdrive file is updated?
'note here that the file name needs to be in lower case, otherwise the result will be False!!
If File.Exists(File.DirAssets, "postcode_lsoa11cd$1.csv") Then
Forgot to say that this code line should be this:My file size will not reach 100 Mb, so I should be OK.
Solved one more problem and that was this:
B4X:'note here that the file name needs to be in lower case, otherwise the result will be False!! If File.Exists(File.DirAssets, "postcode_lsoa11cd$1.csv") Then
I did see some Java code to check this, but didn't work for me.
RBS
If File.Exists(File.DirAssets, "postcode_lsoa11cd$1.csv") Then 'ignore
the file name needs to be in lower case
POSTCODE,LSOA11CDI thought I checked that, and what looked like an uppercase I was actually a lowercase l.
Out of interest, what the heck is that file? Can you post a couple of lines from it?
The letters "cd" were a mild worry, had me expecting files in the order of 100's of MB.
Not sure what went on there, but those files are gone now from assets folder.POSTCODE,LSOA11CD
AB1 0AA,S01006514
AB1 0AB,S01006514
AB1 0AD,S01006514
AB1 0AE,S01006853
It links larger LSOA geographical areas to the smaller postcode areas.
LSOA11CD will kind of give access to health related parameters and other parameters, such as income.
Just found another problem:
I took those files out of the Files Manager tab, but somehow they were still there, so on startup of the app it again populated the tables
after having run the table delete, eg: delete from LSOA11CD.
RBS
Just found another problem:
I took those files out of the Files Manager tab, but somehow they were still there
I don't know the full details but Debug mode uses a virtual assets folder instead of embedding the files in the apk. Most of the code in Debug mode is executed on the PC and not on the device. The device just executes a cutdown shell version of the app that mainly contains UI code to interact with the user.
You can apparently force normal assets behaviour by the IDE attribute
#DebuggerForceStandardAssets: True
But this is only necessary for libraries that try to load files from DirAssets, otherwise it should not be needed.
Not sure what went on there, but those files are gone now from assets folder.
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?