Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Java Development

Reply
Highlighted
Developer
Posts: 80
Registered: ‎05-22-2011
My Device: Blackberry Torch
My Carrier: Airtel
Accepted Solution

Image caching in memory

Hi All,

 

I need to cache the images in my application in memory. Initially I tried to cache it in SDCard. But retrieving images from sdcard seems to be a little bit time consuming as it is a file operation. 

 

The requirement is that when the user takes a screen, the images should be present on the screen if it is available in the cache. Even a slight flickering of the image is not allowed. This is the reason why I want to cache it in memory. 

 

I know that I can cache them in a hastable or something so that the images can be accessed upon giving a key. But I wonder how best BB can manage the memory. What would happen if the cache size increases beyond a particular limit. 

 

Any advises on this will be very helpful.

Thank you very much in advance

Jayakrishnan Salim

Developer
Posts: 19,636
Registered: ‎07-14-2008
My Device: Not Specified

Re: Image caching in memory

I recommend that SD Card approach, My experience is that you can load the images from the SD Card as part of the construction of the Screen, and so there is no flickering seen, since the screen is not pushed until the image is available.

 

I would be interested to understand a scenario in which you think you see flickering.  I suspect some simple re-coding would overcome this issue if in fact it happens. 

 

With respect to keeping the images in memory, this is certainly possible - you need to decide whether to cache the image data or the Bitmap.  The Bitmap will, in almost every case, be significantly larger than the data - but the advantage it has is there is no processing required to turn it into something that you can display on a screen.  I think you will have to test this to determine the best option for you.  For example, if you have large jpgs (by large I mean something like 500 x 500 pixels), then you find the jpeg data is significantly smaller than the Bitmap. 

 

I would suggest that you create some sort of cache that is associated with your Application object, so that when the user closes your application, the cached images are lost and don't take up memory.

 

In order to have some control so you don't kill the BB, I recommend that you implement the LowMemoryManager, so that you can react if the device becomes stressed by your images.

 

If you really want to retain these images over application instances, then you could use RunTimeStore.  But definitely don't do this unless you have your LowMemoryManager working.

 

One 'trick' you might find very useful was to cache both the raw data (on SD Card) and a reference to the Bitmap, using a Weak Reference.  A Weak Reference appears complicated to use, but, once you get your head round it, it is very useful.  IN this case, using it means the system can throw away unreferenced Bitmaps, if it needs to.  If it has thrown the Bitmap away, then the application must get he data off the SD Card (or re-download).  But if it has not thrown one away, then the application can reuse the Bitmap without going to the SD Card.  And the real beauty of this happens on devices with no SD Card, the caching still has a significant effect. 

Developer
Posts: 80
Registered: ‎05-22-2011
My Device: Blackberry Torch
My Carrier: Airtel

Re: Image caching in memory

Thank you very much Peter for the valuable information. 

 

I will explain what I mean by flickering. I am displaying a default image in the bitmap fields when the screen is loaded and later those images are changed to actual images with the help of 4 threads. These 4 threads will either fetch images from cache (images in SDCard) or from server (if it is not available in cache). So, when I do like this, a user can clearly see the default images changing to actual ones. This is not acceptable to my client. He is saying that when a user takes a screen, all the images should be present. 

 

I tried like what you said in the beginning, loading the images as part of the construction of the screen and doing so takes  the screen few more seconds to load which is again unacceptable. When I made an R&D, I could find that file operation takes a little bit time which is delaying the construction of the screen. 

 

This is the reason why I choose to cache the images in memory itself. Fetching from the memory will be much faster than fetching from sdcard. 

 

Now, I changed my code for caching images to something like this:

imageCache.put(2312.jpg, new WeakReference(encodedImage));

 Where imageCache is a static hashtable instance.

 

From whatever I have read from the API documentation of WeakReference object,  the garbage collector can remove these objects when the system is looking for more memory. So, in my case I believe if the cache size (size of the hashtable) exceeds beyond a certain limit, the GC would remove some of the cached images in it. I would like to know your thoughts on this. 

 

In your post you mentioned about LowMemoryManager. Could you please explain how it works? If you have any code snippet, that would be very useful. 

Developer
Posts: 19,636
Registered: ‎07-14-2008
My Device: Not Specified

Re: Image caching in memory

"These 4 threads will either fetch images from cache (images in SDCard) or from server (if it is not available in cache"

 

As noted, in my experience, loading from SD Card has not been a significant issue in terms of performance during Screen construction.  I understand that you have tested this yourself and believe it is. Do you have any figures that you can share?  I would like to know how many images were you loading, what size were these images and how much longer do you think it took?

 

Of course the impact might also be related to the way you are displaying the data.  If you are using a ListField for example, you may not need to load the image until the row containing the image is painted, so you would load fewer images at Screen creation than you would if you have the same number of images being displayed in a VFM.

 

One other important issue here is that you were using a separate Thread to load the images.  The BB device is a single processor.  If your image processing always invokes a Thread to populate the image, then there will always be a delay (and so a flicker) because the processing for the Screen completes before the processing for the image loading starts.  So doing this this way, you are actually costing processor cycles and time delay and causing a flicker.  Perhaps this needs to be revisited?

 

I would try the different logic with your cache,  Your processing can request an image - if the image is present the cache returns the Bitmap.  If not, it starts a Thread.  

 

"He is saying that when a user takes a screen, all the images should be present."

 

I have some difficulties understanding your client position, there are I believe contradictory parts to what your client has asked for:

a) Your client seems to be able to distinguish between images that are downloaded - for which a flicker is acceptable - and ones that are not - for which a flicker is NOT acceptable.  How?  It is not possible for the client to know this. 

b) Your client seems to want the screen display immediately, but at the same time wants the screen display to include all the images.  Only one of these is possible. 

 

Remember you are the expert in BB - you know what the device is capable of doing and what makes a good user experience.  Can I suggest you do not just do it because the client wants something a specific way.  Your job is to create an application that BlackBerry users will want to use, if doing something your client wants will work against that then argue your corner - because in fact, an application that Blackberry users will want to use, is what the client wants too.  Your client will not want exactly what they ask for, if that means the app is not be as useful. 

 

"Fetching from the memory will be much faster than fetching from sdcard."

 

True, but fetching from SD Card is faster than the network, and free.  So I would recommend a three layered cache - memory, SD Card, and then download.  Moreover, this will help the application when then it is stopped and restarted - it will probably not need to download the images again.  Of course you have to implement some sort of old age throw away logic in your cache to remove entries on your SD Card, but the benefits are worth it. 

 

"new WeakReference(encodedImage)"

 

Are you using the EncodedImage directly in the Fields you are displaying?  Remember that a WeakReference will never be cleared while it is actually in use.  So you need to supply exactly the same reference to your BitmapField.  This also means you should pass around a 'final' reference so that the reference itself is always used.  Otherwise the WeakReference may be lost, even though the image it was pointing to, is still available. 

 

"If you have any code snippet, that would be very useful"

 

I can't give you any samples of LowMemoryManager.  In fact I have never used it, I use the WeakReference approach and it seems to work fine - I let the system look after the Bitmaps.  So if you are just just using memory copies and you don't use RuntimeStore, I don't think you will need the LowMemoryManager.  But I can't guarantee that - I might have just been lucky so far. 

Developer
Posts: 80
Registered: ‎05-22-2011
My Device: Blackberry Torch
My Carrier: Airtel

Re: Image caching in memory

"I would like to know how many images were you loading, what size were these images and how much longer do you think it took?"

 

I have around 50 images in a screen and all the images will come under 30-50Kb size. So the threads which I am using will have to fetch all these 50 images from SDCard (if it is available) and show. Though the time taking is less than a second or two to show all of them, there is still some amount of delay which makes the user feels that the images have changed from the default ones. 

 

"Are you using the EncodedImage directly in the Fields you are displaying?  Remember that a WeakReference will never be cleared while it is actually in use.  So you need to supply exactly the same reference to your BitmapField. "

 

I didn't fully understand your comment. I will put my code here. Just look at it and kindly let me know if I am doing anything wrong.

 

For reading from cache:

 

Object object = imageCache.get(imageName); //imageCache is a hashtable
if(object instanceof WeakReference){ WeakReference weakReference = (WeakReference) object; Object obj = weakReference.get(); if(obj instanceof EncodedImage){ EncodedImage encodedImage = (EncodedImage) obj; final Bitmap bitmap = Media.resizeBitmap(encodedImage, height, width); UiApplication.getUiApplication().invokeLater(new Runnable() { public void run() { if(bitmapField != null){ bitmapField.setBitmap(bitmap); } } }); ImageFetcher.this.encodedImage = encodedImage; }else{ fetchImageFromWebService(imageName, photoType, userId, height, width); }

 And for inserting to cache:

 

EncodedImage encodedImage = EncodedImage.createEncodedImage(imageBytes, 0, imageBytes.length);
		imageCache.put(imageName, new WeakReference(encodedImage));

 

 

Developer
Posts: 19,636
Registered: ‎07-14-2008
My Device: Not Specified

Re: Image caching in memory

My understanding of Weak References suggests that what you have here may not work very well. 

 

The Object that is actually used is the Bitmap.  The Weak Reference will not be freed as long as it is in use.  IN this case the Weak Reference is never used anywhere else in your program.  So the garbage Collector could in fact free the EncodingImage anytime it wants, and break your WeakReference.

 

Instead I would keep your Bitmap in the Cache (already scaled appropriately).  Then the cache will deliver a reference to that Bitmap which the program uses it - the same reference it already has. Then the garbage collector will not free the Bitmap until the program has finished using it, and even then the garbage collector may not free it.

 

So this mechanism is useful in cases where you have the possibility of numerous images, and you want to keep the ones used most often in memory.  Say for example, you have news feed, and in the news feed, the user can choose to display news on sport, or local or politics or...  The user chooses sport, which downloads all the sport images.   Then they choose politics, then they go back to sport.  Chances are that the sport downloads will still be in the cache.  This mechanism is even more useful if you offer a 'refresh function, so that the user on sport, can hit refresh and get the latest text, retaining all the images (and getting the new ones). 

 

But if you are using 50 images, and it is always the same 50 images, then I am not sure that a cache like this is best design for your application.