B4A Library [Lib] Cache

Discussion in 'Additional libraries, classes and official updates' started by Informatix, Sep 29, 2012.

  1. Informatix

    Informatix Expert Licensed User

    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:
    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:
    Code:
    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.
     

    Attached Files:

    Last edited: May 14, 2017
  2. MaxApps

    MaxApps Active Member Licensed User

    Can I use this cache to avoid using the built in cache?
    I have an app, that handles a lot of small bitmaps and on some devices, it keeps on running out of cache.

    Kind regards
    Jakob
     
  3. Informatix

    Informatix Expert Licensed User

    Could you tell me more? I don't know what's this built-in cache you're talking about. The only thing I know that uses a cache in B4A is the WebView. I'm not sure you can override its caching functions and I'm not sure that doing this would be a good idea.
     
  4. Informatix

    Informatix Expert Licensed User

    New version

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

    Informatix Expert Licensed User

    List of properties and methods (v1.11):
    • Cache
      Events:
      • CopyDone(Key As String, Error As Boolean)
      • PutDone(Key As String, Error As Boolean)
      • GetDone(Key As String, Obj As Object)
      Properties:
      • BufferSize As Int
        Gets/sets the buffer size of the disk cache. By default, it is set to the block size of the filesystem.
      Methods:
      • BitmapSize (bitmap As BitmapWrapper) As Long
        Returns the size in bytes of the given bitmap.
      • ClearDiskCache
        Deletes all files in the cache directory including files that weren't created by the cache.
      • ClearMemoryCache ()
        Clears the memory cache holding bitmaps.
      • CloseAndClearDiskCache
        Closes the disk cache and deletes all files in the cache directory including files that weren't created by the cache.
      • CloseDiskCache
        Closes the disk cache. Stored values will remain on the filesystem.
      • CopyFileToDisk (key As String, dir As String, filename As String)
        Copies a file in the disk cache.
      • CopyFileToDisk_Async (key As String, dir As String, filename As String, eventPrefix As String)
        Copies a file in the disk cache (asynchronously).
        eventPrefix = prefix of the "CopyDone" event.
        An event is triggered when the job ends. You can get it with:
        Sub eventPrefix_CopyDone(key As String, Error As Boolean)
        Error is true if there was an error.
      • DiskFree As Long
        Returns the available space in bytes of the disk cache.
      • DiskJournal As List
        Returns a copy of the journal of the disk cache.
        Example:
        Code:
        Dim l As List
        l = myCache.DiskJournal
        For i = 0 To l.Size - 1
           
        Log(l.Get(i))
        Next
      • DiskList As Map
        Returns a list of the current contents of the disk cache, unordered.
        Map structure: key=filename, value=object array (0=time of the last modification, 1=size in bytes)
        Example:
        Code:
        Dim m As Map
        m = myCache.DiskList
        Dim Info(2As Object
        For i = 0 To m.Size - 1
           
        Log(m.GetKeyAt(i))
           Info = m.GetValueAt(i)
           
        Log("   " & DateTime.Date(Info(0)) & " " & DateTime.Time(Info(0)))
           
        Log("   " & Info(1) & " bytes")
        Next
      • DiskUsed As Long
        Returns the sum of the sizes in bytes of the files in the disk cache.
      • GetBitmap (key As String, updateMemory As Boolean) As Bitmap
        Returns the bitmap for key from one of the caches (the memory cache is looked first).
        Returns null if the key was not found.
        The bitmap source (memory or disk cache) is specified in the log.
        updateMemory = if true, the memory cache is updated if the bitmap was only on disk.
      • GetBitmapFromDisk (key As String) As Bitmap
        Returns the bitmap for key from the disk cache.
        Returns null if the key was not found.
      • GetBitmapFromDisk_Async (key As String, eventPrefix As String)
        Returns the bitmap for key from the disk cache (asynchronously).
        eventPrefix = prefix of the "GetDone" event.
        An event is triggered when the job ends. You can get it with:
        Sub eventPrefix_GetDone(key As String, bmp As Bitmap)
        bmp is null if the key was not found or there was an error.
      • GetBitmapFromMemory (key As String) As Bitmap
        Returns the Bitmap for key if it exists in the cache.
        Returns null if not found.
      • GetBitmap_Async (key As String, updateMemory As Boolean, eventPrefix As String)
        Returns the given bitmap for key from one of the caches.
        If the bitmap is in memory, it is sent back immediately. Otherwise, it is loaded from disk asynchronously.
        updateMemory = if true, the memory cache is updated if the bitmap was only found on disk.
        eventPrefix = prefix of the "GetDone" event.
        An event is triggered when the job ends. You can get it with:
        Sub eventPrefix_GetDone(key As String, bmp As Bitmap)
        bmp is null if the key was not found or there was an error.
      • GetDirectory As String
        Returns the directory where the disk cache stores its data.
      • GetObjectFromDisk (key As String) As Object
        Returns the object for key from the disk cache.
        Returns null if the key was not found.
      • GetObjectFromDisk_Async (key As String, eventPrefix As String)
        Returns the object for key from the disk cache (asynchronously).
        eventPrefix = prefix of the "GetDone" event.
        An event is triggered when the job ends. You can get it with:
        Sub eventPrefix_GetDone(key As String, obj As Object)
        obj is null if the key was not found or there was an error.
      • GetStringFromDisk (key As String) As String
        Gets the string for key from the disk cache.
        Returns an empty string if the key was not found.
      • Initialize (memoryCachePct As Byte, diskCacheSize As Long, diskCacheDir As String)
        Initializes the caches.
        memoryCachePct: percentage of the available memory, used for the memory cache (this cache is only used for bitmaps, the other objects are stored on disk). Cannot exceed 75.
        diskCacheSize: size in bytes of the cache on disk
        diskCacheDir: folder where cache files are stored
        If empty, the default cache folder is used. This folder is specific to the application.
        Example:
        Code:
        '25% for the memory cache and 20MB for the disk cache
        myCache.Initialize(2520 * 1024 * 1024"/mnt/sdcard/myAppCache")
      • IsClosed As Boolean
        Returns true if the disk cache has been closed.
      • IsInMemory (key As String) As Boolean
        Returns true if the Bitmap for key is still in the memory cache.
      • IsOnDisk (key As String) As Boolean
        Returns true if the file for key is still in the disk cache.
      • MemoryEvictionCount As Int
        Returns the number of bitmaps that have been evicted from the memory cache.
      • MemoryFree As Int
        Returns the available space in bytes of the memory cache.
      • MemoryHitCount As Int
        Returns the number of times GetBitmapFromMemory returned a bitmap that was present in the cache.
      • MemoryList As Map
        Returns a copy of the current contents of the memory cache, ordered from least recently accessed to most recently accessed.
      • MemoryMissCount As Int
        Returns the number of times GetBitmapFromMemory returned null (bitmap not found).
      • MemoryPutCount As Int
        Returns the number of times PutBitmapInMemory was called.
      • MemoryUsed As Int
        Returns the sum of the sizes in bytes of the entries in the memory cache.
      • PutBitmapInMemory (key As String, bitmap As BitmapWrapper) As Boolean
        Caches the given bitmap in memory.
        Returns false if the bitmap is too big for the cache.
      • PutBitmapOnDisk (key As String, bitmap As BitmapWrapper, format As String, quality As Int)
        Stores the given bitmap in the disk cache.
        Format: "JPEG" or "PNG"
        Quality: quality of JPEG compression (0..100). This setting is ignored for the PNG format.
      • PutBitmapOnDisk_Async (key As String, bitmap As BitmapWrapper, format As String, quality As Int, eventPrefix As String)
        Stores the given bitmap in the disk cache (asynchronously).
        Format: "JPEG" or "PNG"
        Quality: quality of JPEG compression (0..100). This setting is ignored for the PNG format.
        eventPrefix = prefix of the "PutDone" event.
        An event is triggered when the job ends. You can get it with:
        Sub eventPrefix_PutDone(key As String, Error As Boolean)
        Error is true if there was an error.
      • PutObjectOnDisk (key As String, obj As Object)
        Stores the given object in the disk cache.
        This works only with serializable objects (numbers, booleans, lists, arrays...).
      • PutObjectOnDisk_Async (key As String, obj As Object, eventPrefix As String)
        Stores the given object in the disk cache (asynchronously).
        This works only with serializable objects (numbers, booleans, lists, arrays...).
        eventPrefix = prefix of the "PutDone" event.
        An event is triggered when the job ends. You can get it with:
        Sub eventPrefix_PutDone(key As String, Error As Boolean)
        Error is true if there was an error.
      • PutStringOnDisk (key As String, value As String)
        Stores the given string in the disk cache.
      • RemoveBitmapFromMemory (key As String)
        Removes the Bitmap for key if it exists.
      • RemoveFromDisk (key As String) As Boolean
        Removes the file for key if it exists in the disk cache.
        Returns true if the file was removed.
     
    Last edited: May 5, 2013
  6. Informatix

    Informatix Expert Licensed User

    New version

    v1.11:
    Fixed a minor bug.
     
  7. Shark812

    Shark812 Member Licensed User

    Thank you for this and awesome demo code. Very clear.
    Was able to integrate it very quickly with the app I'm developing.

    I will be donating a couple $ later today ;)
    And more once my app is ready for release, since your tutorials have been helping as well. :sign0098:
     
  8. Informatix

    Informatix Expert Licensed User

    Thank you.
     
  9. Shark812

    Shark812 Member Licensed User

    Need Assistance...

    Okay, I've got an issue that's driving me insane and I keep thinking it's something simple that I'm overlooking but can't figure it out!

    The imageview is being shrunken(?) when using GetBitmap(I suppose?), only after closing/resuming activity. (rotate or userclose/reopen)
    It does not have this problem using Android 2.2, but does on 4.0.3 and 4.1.2, using emulators and devices.

    Also, I would think this problem would be seen in your demo code, but it doesn't occur! And I can't figure out what I'm doing different...

    Screenshots: Before and After

    I've uploaded a very small sample project that will demonstrate my problem.
    Any help is greatly appreciated.

    Edit: Also, here is the code, if it can be figured out by just looking at it...
    Code:
    'Activity module
    Sub Process_Globals
       
    'These global variables will be declared once when the application starts.
       'These variables can be accessed from all modules.
       Dim Cache As Cache
       
    Dim MemorySize As Int: MemorySize = 50
       
    Dim DiskSize As Int: DiskSize = 2 * 1024 * 1024
    End Sub

    Sub Globals
       
    'These global variables will be redeclared each time the activity is created.
       'These variables can only be accessed from this module.
       Dim ivTest As ImageView
       
    Dim ivThumbnailColor As Int: ivThumbnailColor = Colors.DarkGray
    End Sub

    Sub Activity_Create(FirstTime As Boolean)
       
    'Do not forget to load the layout file created with the visual designer. For example:
       'Activity.LoadLayout("Layout1")
       
       
    Dim btnLoad As Button
       btnLoad.Initialize(
    "btnLoad")
       btnLoad.Text = 
    "Load Image"
       
    Activity.AddView(btnLoad, 30dip60dip165dip45dip)
       
       
    Dim btnClear As Button
       btnClear.Initialize(
    "btnClear")
       btnClear.Text = 
    "Clear Cache"
       
    Activity.AddView(btnClear, 30dip10dip175dip45dip)
       
       ivTest.Initialize(
    "")
       ivTest.Gravity = 
    Gravity.FILL
       ivTest.Color = ivThumbnailColor
       
    Activity.AddView(ivTest, 100%x - 95dip20dip65dip65dip)
       
       
    Cache.Initialize(MemorySize, DiskSize, "")
       
       WantImage2(
    "Testing""http://api.jamendo.com/get2/image/album/redirect/?id=116&imagesize=65"False)
    End Sub

    Sub Activity_Resume

    End Sub

    Sub Activity_Pause (UserClosed As Boolean)
       
    Cache.CloseDiskCache
    End Sub

    Sub JobDone (Job As HttpJob)
        
    Log("JobName = " & Job.JobName & ", Success = " & Job.Success)
        
    If Job.Success = True Then
            
    Select Job.JobName
             
    Case "Testing"
                ivTest.Bitmap = Job.GetBitmap
                
                
    Cache.PutBitmapInMemory(Job.JobName, ivTest.Bitmap)
                
    Cache.PutBitmapOnDisk(Job.JobName, ivTest.Bitmap, "JPEG"100)
            
    End Select
        
    Else
            
    Log("Error: " & Job.ErrorMessage)
            
    ToastMessageShow("Error: " & Job.ErrorMessage, True)
        
    End If
        Job.Release
    End Sub

    Sub WantImage2(ImageKey As String, URL As String, Refresh As Boolean) As Bitmap
       
    ' The memory cache is looked first. If nothing is found, the disk cache is used.
       Dim bmp As Bitmap, iv As ImageView
       bmp = 
    Cache.GetBitmap(ImageKey, True)
       iv = ivTest
       
       
    If bmp.IsInitialized AND Refresh <> True Then
          
    ' The bitmap is in cache -> the image is displayed
          ' Also, refresh must be false.
          iv.Bitmap = bmp
       
    Else
          
    ' The bitmap is not in cache -> lets download the image
          ' An hourglass is displayed in the meanwhile
          iv.Bitmap = LoadBitmap(File.DirAssets, "hourglass.png"'Source: http://sweetclipart.com/hourglass-silhouette-874
          Dim job As HttpJob
          job.Initialize(ImageKey, Me)
          job.Download(URL)
       
    End If
    End Sub

    Sub btnLoad_Click
       WantImage2(
    "Testing""http://api.jamendo.com/get2/image/album/redirect/?id=116&imagesize=65"False)
    End Sub

    Sub btnClear_Click
       
    Cache.ClearMemoryCache
       
    Cache.ClearDiskCache
    End Sub
     

    Attached Files:

    Last edited: Oct 22, 2012
  10. Informatix

    Informatix Expert Licensed User

    It's because of the Gravity of the ImageView. It is lost (I don't know why and it's not related to my code). Put ivTest.Gravity = Gravity.FILL after each iv.Bitmap = bmp and that should fix the problem.
     
  11. Shark812

    Shark812 Member Licensed User

    Thanks

    Thanks again! I could have swore I tried that but I obviously did not..
     
  12. bloxa69

    bloxa69 Active Member Licensed User

    Hey Informatix,
    I've played with your lib, it works great, no problems.
    I've only have a hard time checking the images in cache versus the remote originals, may be I am missing something. For my app, I need to compare the modification dates on the remote images versus the ones in cache and then refresh only those images in cache that have been updated on the remote server.

    I was looking for some properties or methods to get the bitmap info from cache by using the bitmaps's key (because that's the only way to identify them), something like myCache.DateModified(key as String) or myCache.GetBitmapProperties(key as as String) but couldn't find any easy way of doing it.
    Anything I could find so far was IsOnDisk (key As String) property that is using this approach. May be I am missing something?

    Thanks for the lib.
     
  13. Informatix

    Informatix Expert Licensed User

    Bitmaps are stored as files in the disk cache, so to know their timestamp, read the file timestamp. These files are named with their key. The extension is 0 (example: myimage125.0).
     
  14. bloxa69

    bloxa69 Active Member Licensed User

    Thanks for the advice Informatix. I figured that though. So for now I am downloading the bitmaps to the app's cache folder using httputils and later on when needed I check the file's properties using the regular file operations. But I don't need the cache lib for that, I guess.

    Thank you for sharing your work.
     
  15. Informatix

    Informatix Expert Licensed User

    There's a common confusion between a cache folder and a temporary folder. A cache folder uses a caching algorithm to handle its content (LRU for my library). A temporary folder is just a folder. If you download directly the images in the cache folder, then my library is of no use to you.
     
  16. bloxa69

    bloxa69 Active Member Licensed User

  17. Informatix

    Informatix Expert Licensed User

    New version 1.34:

    The Cache library is now totally free.

    In this version:
    I improved some functions and fixed minor bugs.
    I added two properties: FreeMemory and MaxMemory.
     
  18. guidoarfini

    guidoarfini Member Licensed User

    Hello Guys!
    With this is library can i get chace only my application or can i get any cache list stored in my phone?

    I would like to create a mini application to save and transport the advances of my games in another phone without using root permissions.
     
  19. Informatix

    Informatix Expert Licensed User

    What you call "cache" is just the private folder of an application, so it has nothing to do with this library.
    And your project cannot work without being root because of the rights on this private folder.
     
  20. guidoarfini

    guidoarfini Member Licensed User

    tank you
     
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice