B4A Library [Lib] Cache

This library adds a LRU cache to your application. It is based upon the memory cache introduced in API 12 (Honeycomb) and the disk cache introduced in API 14 (ICS). The library is thread-safe and works with Android versions > 1. The memory cache accepts only bitmaps. The disk cache accepts serializable objects (strings, numbers, lists, arrays...) and bitmaps.

If you're wondering what's a cache and if you need it, here's the Wikipedia definition:
[...] A cache is a component that transparently stores data so that future requests for that data can be served faster. The data that is stored within a cache might be values that have been computed earlier or duplicates of original values that are stored elsewhere. If requested data is contained in the cache (cache hit), this request can be served by simply reading the cache, which is comparatively faster. Otherwise (cache miss), the data has to be recomputed or fetched from its original storage location, which is comparatively slower. Hence, the greater the number of requests that can be served from the cache, the faster the overall system performance becomes. [...]

This cache uses the LRU (Least Recently Used) algorithm. Each time a value is stored or accessed, it is moved to the head of a queue. When a value is added to a full cache, the value at the end of that queue is evicted and may become eligible for garbage collection.

A cache may be useful in various situations. Its common use is for downloaded resources or image galleries, but you can also use it in a state manager, or in a game to improve performance.
The demo should prove you the benefit of using a cache.

List of properties and methods

If you want to put the disk cache on an external storage media, don't forget to add the following permission with the Manifest editor:
B4X:
AddPermission("android.permission.WRITE_EXTERNAL_STORAGE")

v1.1:
I added statistics for the memory cache (miss count, hit count, etc.)
I added the IsClosed function for the disk cache.

v1.11:
Fixed a minor bug.

v1.34:
I improved some functions and fixed minor bugs.
I added two properties: FreeMemory and MaxMemory.

v1.35:
I improved the synchronization of asynchronous operations.

v1.36:
I fixed a rare bug with very large caches.
I made an internal change for UltimateListView.
I updated the deprecated links of the demo.

v1.37:
I fixed a bug with truncated lines.
I ignore any error resulting from a call to StatFs because this OS library does not work properly on some devices. I use a default value when an error occurs.
The key for the disk cache accepts longer names.

v1.38:
I fixed a bug with the default cache folder (this concerns mainly the ULV users).
I also changed the GetBitmap function so the message about the source is sent to the unfiltered log now, instead of the filtered log.
 

Attachments

  • Cache v1.38.zip
    74.3 KB · Views: 558
  • SourceJava_Cache_1_38.zip
    51.1 KB · Views: 225
Last edited:

gigi0ne

Member
Licensed User
Longtime User
hi informatix,

apologise for my bad english, it's possible to have a version without key but with filename?
i need to insert and read image but don't have a sequential order...

Grazie, ciao
 

Informatix

Expert
Licensed User
Longtime User
hi informatix,

apologise for my bad english, it's possible to have a version without key but with filename?
i need to insert and read image but don't have a sequential order...

Grazie, ciao
Files in cache are named from the key. The extension 0 is just added. So if you use the key "toto", the corresponding file is "toto.0".
Keys do not have to be in sequential order. They just have to be unique.
 

gigi0ne

Member
Licensed User
Longtime User
Only one info, this is my code

B4X:
    x = DateTime.Now
    FN = IMG&".png"
    If MCache.IsOnDisk(IMG) Then
        L_Bitmap = MCache.GetBitmap(IMG,True)
    Else
        If File.Exists(File.DirAssets, FN) Then
              L_Bitmap.Initialize(File.DirAssets,FN)
              MCache.PutBitmapOnDisk(IMG, L_Bitmap,"PNG",100)
        Else
              If MCache.IsOnDisk("default") Then
                    L_Bitmap = MCache.GetBitmap("default",True)
              Else
                    L_Bitmap.Initialize(File.DirAssets,"default.png")
                    MCache.PutBitmapOnDisk("default", L_Bitmap,"PNG",100)
              End If    
          End If  
    End If
    Log("Create item End: " & (DateTime.Now-x))

    ...

    L_ImageView.Initialize("")
    L_Panel1.Color=Colors.black  
    L_Panel1.AddView(L_ImageView,5dip,5dip,65dip,65dip)
    L_ImageView.Bitmap=L_Bitmap

the first time with L_Bitmap.Initialize(File.DirAssets,FN), the image it's correct visualized (65x65),
the second time, using bitmap from cache the image it's visualized similar to icon (32x32)

any idea?

Thank's
Luigi
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
Only one info, this is my code

B4X:
    x = DateTime.Now
    FN = IMG&".png"
    If MCache.IsOnDisk(IMG) Then
        L_Bitmap = MCache.GetBitmap(IMG,True)
    Else
        If File.Exists(File.DirAssets, FN) Then
              L_Bitmap.Initialize(File.DirAssets,FN)
              MCache.PutBitmapOnDisk(IMG, L_Bitmap,"PNG",100)
        Else
              If MCache.IsOnDisk("default") Then
                    L_Bitmap = MCache.GetBitmap("default",True)
              Else
                    L_Bitmap.Initialize(File.DirAssets,"default.png")
                    MCache.PutBitmapOnDisk("default", L_Bitmap,"PNG",100)
              End If   
          End If 
    End If
    Log("Create item End: " & (DateTime.Now-x))

    ...

    L_ImageView.Initialize("")
    L_Panel1.Color=Colors.black 
    L_Panel1.AddView(L_ImageView,5dip,5dip,65dip,65dip)
    L_ImageView.Bitmap=L_Bitmap

the first time with L_Bitmap.Initialize(File.DirAssets,FN), the image it's correct visualized (65x65),
the second time, using bitmap from cache the image it's visualized similar to icon (32x32)

any idea?

Thank's
Luigi
Don't use Bitmap.Initialize because, like LoadBitmap or LoadBitmapSample, it alters the density of images. Use the function LoadScaledBitmap of my Accelerated Surface library instead.
 

Mrjoey

Active Member
Licensed User
Longtime User
Hey , Does cache solves outofmemory from horizontalscrollview ?
horizontalscrollview is displaying images horizontaly..thank u
 

Mrjoey

Active Member
Licensed User
Longtime User
im trying to save a bitmap from assets file wish is 82kb (428*351)
B4X:
Cache.PutBitmapOnDisk_Async(i,LoadBitmapSample(File.DirAssets,"coverbig.png",154dip,130dip),"PNG", 100, "Cache")
im getting in the log screen : downsampling image due to lack of memory....
otherewise all is done in async , how to fix downsampling
 

Informatix

Expert
Licensed User
Longtime User
Hey , Does cache solves outofmemory from horizontalscrollview ?
horizontalscrollview is displaying images horizontaly..thank u
No, the main goal is to store local copies of remote files or modified/resized images (so you don't have to load/modify/resize them again).
 

Informatix

Expert
Licensed User
Longtime User
im trying to save a bitmap from assets file wish is 82kb (428*351)
B4X:
Cache.PutBitmapOnDisk_Async(i,LoadBitmapSample(File.DirAssets,"coverbig.png",154dip,130dip),"PNG", 100, "Cache")
im getting in the log screen : downsampling image due to lack of memory....
otherewise all is done in async , how to fix downsampling
If you want to avoid downsampling, don't use the LoadBitmap or LoadBitmapSample functions. Use instead the LoadScaledBitmap function of my Accelerated Surface library. It will raise an Exception if you're short on memory (to catch with Try/Catch).
 

Mrjoey

Active Member
Licensed User
Longtime User
If you want to avoid downsampling, don't use the LoadBitmap or LoadBitmapSample functions. Use instead the LoadScaledBitmap function of my Accelerated Surface library. It will raise an Exception if you're short on memory (to catch with Try/Catch).
if i load bitmaps with loadscaledbitmap and added them to a horizontalscrollview the outofmemory disapears?
 

Mrjoey

Active Member
Licensed User
Longtime User
do u agree that horizontalscrollview cant handles bitmaps? about 70 scaled bitmaps?i think i have to replace it with b4agallery library to display horizontal bitmaps
 

Mrjoey

Active Member
Licensed User
Longtime User
because i loaded about 70 scaled bitmaps using cache to a horizontalscrollview using HSV.panel.addview....the loading is doing well but when i close the activity i load some bitmaps to coverflow or b4agallery then i get outofmemory from coverflow....
 

Informatix

Expert
Licensed User
Longtime User
because i loaded about 70 scaled bitmaps using cache to a horizontalscrollview using HSV.panel.addview....the loading is doing well but when i close the activity i load some bitmaps to coverflow or b4agallery then i get outofmemory from coverflow....
The lonleystar's Coverflow lib loads probably all images in memory before displaying them so you run out of memory after a certain quantity is loaded (70 on your device). It's normal. And it's not a storage cache that will do any difference. You have to write your view differently to save as memory as possible. My own version of the Coverflow lib has this feature (it can display theoretically an unlimited number of bitmaps) but it's not available for free.
 

Mrjoey

Active Member
Licensed User
Longtime User
if u load a bitmap from a diskcache the bitmap does exist in the memory? does android:largeHeap="true" solves the problem?
 

Informatix

Expert
Licensed User
Longtime User
if u load a bitmap from a diskcache the bitmap does exist in the memory?
Of course. Where do you think that the bitmap is decoded and stored while loading?

does android:largeHeap="true" solves the problem?
It just allows you to have more memory at your disposal. If your folder contains 3500 pictures, and LargeHeap = True, maybe you will be able to display the first 2000, but not the following. For a gallery, it's a bad workaround, not a definitive solution.
 

maxtech1988

New Member
Licensed User
Longtime User
Hello guys,

is possible to use putbitmapondisk incrementing a progressbar?

At the moment, i try to use the PB.Progress = Value.

But the progressbar only is updated after the last copy from cache.

Follow a sample from my code:

B4X:
Sub LoadImages    
    Dim bmp As Bitmap
    For looping = 1 To 100
        PB.Progress = looping
        bmp = LoadBitmap(File.DirAssets,"hd_" & looping & ".png")
        Cache.PutBitmapOnDisk(looping, bmp, "PNG", 100)
    Next
End Sub

Thanks!
 

Mrjoey

Active Member
Licensed User
Longtime User
Hello guys,

is possible to use putbitmapondisk incrementing a progressbar?

At the moment, i try to use the PB.Progress = Value.

But the progressbar only is updated after the last copy from cache.

Follow a sample from my code:

B4X:
Sub LoadImages  
    Dim bmp As Bitmap
    For looping = 1 To 100
        PB.Progress = looping
        bmp = LoadBitmap(File.DirAssets,"hd_" & looping & ".png")
        Cache.PutBitmapOnDisk(looping, bmp, "PNG", 100)
    Next
End Sub

Thanks!
u could use PutBitmapOnDisk_Async
and on



    • PutDone (Key As String, Error As Boolean) event and u make a counter to fill the progressbar
    • or use doevents
 
D

Deleted member 30048

Guest
hello, I want to use this library to improve performance but I have a problem. I tested the library with a very large image of 2700x1200 pixels (in the final app I will use less resolution). The problem is that every time I rotate the phone the memory RAM consumption increases until I get a out of memory message. It's like every time I rotate the phone the image is loaded again rather than use what's already there.

The code I used is the next:

B4X:
MyImageView.Bitmap = LoadImageFromCache("ImageName")

Sub LoadImageFromCache(ImageName As String) As Bitmap
    Dim bmp As Bitmap
    bmp = Cache.GetBitmap(ImageName, True)
    If bmp.IsInitialized == False Then
        bmp = LoadBitmap(File.DirAssets, ImageName & ".png")
        Cache.PutBitmapInMemory(ImageName, bmp)
        Cache.PutBitmapOnDisk_Async(ImageName, bmp, "PNG", 100, "Cache")
    End If
  
    Return bmp
End Sub

What am I doing wrong?

NOTE: The imageview is created by designer.

Best regards!
 
Top