When to Load image in ListView? - android

When I use ListView the getView() method is called many times. Every time when the getView() is called i load the image with Asyc task. I mean every time i reset the image which is annoying.
How to understand when to load the image?

You should cache loaded images, by storing i.e. on SD card, so once you got a copy there, no need to download it again. There's lot of ready-to-use classes that can do the job for you, like:
http://greendroid.cyrilmottier.com/reference/greendroid/widget/AsyncImageView.html

you must must have two flags.
One which says if you've already loaded the image, if true you do nothing.
One which says if you're currently loading the image, if true you do nothing.
The members will also help you on maintaining the state of the image.
Your code should look something like this:
private boolean isLoading = false;
private boolean hasLoaded = false;
if(!hasLoaded){
if(!isLoading){
isLoading = true;
//do async load
//on positive completition callback set hasLoaded to true
//on negative completition callback set isLoading to false
}
}

One of the best solution is to create image cache using the WeakReference. This way you can keep images in memory and only need load from server when they are not in memory. In this method the image would be removed from the memory when system encounter low memory situation. So your current activity would always keep the hard reference to the bitmap's required and the image cache would keep the weak reference to the bitmap's.
below reference links will help you
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
http://www.codeproject.com/Articles/35152/WeakReferences-as-a-Good-Caching-Mechanism

the Volley library (made by google) has a very intuitive class for an imageView that can have a url , called "NetworkImageView" .
you should check it out and watch the video, since they show that it's quite annoying to do it using asyncTask (plus the asyncTask is known to have a limit of tasks, about 255 or so) .
for setting the url, just use setImageUrl .
it has some useful methods for the phases of loading too: setDefaultImageResId , setErrorImageResId.
it's also supposed to have built in caching mechanism of some sort, but i haven't read much about it, so you might want to check out their samples.
this will remove the need to use asyncTasks for the listView's items.
one of my questions regarding the volley includes a sample code , here .

You can add a caching layer and optionally preloading the images. A good strategy for caching Images (Bitmap objects to be exact) is to use a strategy called LRU or least recently used.
Android support library has a class called LruCache that implements this strategy. So for example, when you download/load the image for the first time, you stick it into the cache. later, you can first check if it's already in cache and load it from there.
For preloading, A good rule of thumb is to preload the previous ten and the next ten items.

Related

ObjectBox lazyList behaviour

I'm not sure I understand from the documentation how should I use the Lazy List.
What the different between findLazy() and findLazyCached() the function description is exactly the same.
Should I make a find() query first time and just then use findLazy()?
Example of using:
Box<FastCacheData> box = box.boxFor(FastCacheData.class);
LazyList<FastCacheData> build = box.query().build().findLazy();
What the different between findLazy() and findLazyCached() the function description is exactly the same.
They both return a LazyList, which will only load the member objects as they're each accessed. The difference between the two is that the cached version will cache the object so that further accesses won't result in extra loads - the non-cached version will load a fresh object every time.
Should I make a find() query first time and just then use findLazy()
It's a question of when you want the loading to happen. If you want the whole thing loaded when the find() call is made, use the find() call. Else if you want to defer the loading to when you access the data, use the findLazy() call.

Clear glide cache across all activites?

I have the user's profile pic across multiple activities in my app. Once, they change their profile image, I want to make sure that all my Glide instance's cache are cleared. That way when they navigate around the app, they can see their updated profile pic.
Currently I'm using this method: Glide.get(activity).clearDiskCache(); and that only clears the Glide cache for that activity and not across my app.
Hope someone has a quick solution, where I don't need to call the .signature() function for each glide instance in each of my activites. Or clear each glide cache in each activity.
Try
Glide.get(context).clearMemory();
OR
Glide.get(context).clearDiskCache();
Note: clearMemory() must be called on the main thread. clearDiskCache() must be called on a background thread. You can't call both at once on the same thread.
I went through this whole windmill trying Signatures and clearing caches, and to be honest - none of those options work particularly well and they're usually slow.
Glide's first recommended solution is bar far superior, although it can sometimes take a bit more time to rework your code. I eventually lost my marbles and made the necessary changes to my code. It was well worth it.
Solution: Change the image name of the image when the user uploads a new image. Get the file name and use that. Once the image URL has changed, Glide understands you have changed the image and will update the Cache accordingly.

Spawning numerous AsyncTask instances - implications?

I am designing an Android application targeting >= API 17. I have created a class, DownloadImageTask which extends AsyncTask, and receives a string (URL) and an ImageView as arguments. In it, I am opening an HTTP connection, downloading an image from a URL, and using BitmapFactory to create a Bitmap object from the data, then setting the bitmap to the ImageView. The end result is a populated list of data which is available to the user to scroll through, with images populating as they can.
This appears to be a good design on the surface - but I am concerned that I am putting my app at risk for an OOM condition, or other violation of the user experience rules. I'd like to know if the way I've designed this is correct, or if not, how I should approach this.
Thank you in advance for your help.
Two considerations to your own approach:
You shouldn't pass the ImageView to the async task because in that way you are coupling your view and your service layer. So send to the async task the URL, and onPostExecute method call to Activity which implement an updateView (or the like) method.
About your OOM, you are right. The problem might arise if you use the original bitmaps which could have larger resolution than required. Therefore you should scale down the images you keep in memory.
The last issue might not be difficult if you use a few images otherwise could be problematic. So if you will be working with a lot of images and you are not forced to implement your own version, you should have a look to the existing libraries. Some are already mentioned:
Glide
Picasso

Best practice for storing images to use on short term

So, I'm using custom number tiles for images. The images are stored as resources. I've been tracing a memory leak, and have cause to believe the method I am storing these images for use is suspect. Currently, I'm doing this:
private void loadImageList()
{
Log.d(TAG,"Reloading List "+imageList);
if (imageList==null || imageList.size()<10)
{
imageList=new ArrayList<Bitmap>(10);
imageList.add(0,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number0));
imageList.add(1,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number1));
imageList.add(2,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number2));
imageList.add(3,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number3));
imageList.add(4,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number4));
imageList.add(5,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number5));
imageList.add(6,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number6));
imageList.add(7,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number7));
imageList.add(8,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number8));
imageList.add(9,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number9));
}
}
public void onStart()
{
super.onStart();
loadImageList();
}
What will happen is if I open and close this application repeatedly, then the system won't have enough memory to add an image to the ImageList. I'm setting the image of the button like this (But with an object ImageButton, of course). I should add that the application is threaded, and this call resides in a runOnUiThread(Runnable)
ImageButton.setImageBitmap(imageList.get(current_var));
I've tried deleting the images in the onStop() command, but it will sometimes cause a crash when the thread tries to allocate the image stored in memory to the button, due to the threaded nature of the beast.
So, is there a better way that I can load these images that won't cause a memory leak?
I realized something after posting this. I could just make the imageList static. These images aren't going to change from one view to the next. The Bitmaps don't contain a view, so the memory won't lock up. I'm already checking to see if the values exist before I'm using them. And this function is used so frequently that holding on to static memory won't hurt other aspects of the program. Sometimes it just helps to put it into words..

ListView asynchronous image loading strategy

I currently have a ListView with a custom adapter that gets information describing the content of the rows asynchronously. Part of each row is an image URL, that I'm planning to download asynchronously and then display.
My current plan for a strategy to download these images is:
Keep a cache of soft references to downloaded Bitmap objects.
When a getView() is called and the bitmap is in the cache, set the bitmap for the ImageView directly.
If the bitmap isn't in the cache, start loading it in a separate thread, after the download is complete add it to the cache and call notifyDataSetChanged() on the adapter.
I am also planning to kill pending downloads when the Activity object owning the ListView's onDestroy()-method (Or possibly even in the onPause()-method) is called, but most importantly I want to kill the download of pending images when the row goes off screen. I might only actually cancel the download after a short delay, so it can be resumed without wasting bandwidth if the row comes on-screen quickly again.
I, however, am unsure about a few things:
What is the best way to detect when a row goes off-screen so I can cancel the download?
Is calling notifyDataSetChanged() the best thing to do after the download has completed or is there a better way?
Also any comments on the whole strategy would be appreciated.
I don't think calling notifyDataSetChanged() is really needed... I would do it like that:
store URL as Tag in the view when created/updated
register a listener in downloader thread (async task???) for download keeping reference to the view and the URL
whenever image is downloaded asynchronously, I check TAG in the view and if it matches - i would update the ImageView (important to do it in UI thread, but when using async task, it is given). The image should also be stored on SD card (and every time you request URL you should check if it is not already downloaded).
every time when getView() reuses the view (passed view is not empty) I would check the Tag (old URL), replace it with the new URL and cancel the download of the oldURL.
I think it would be pretty much it (some corner cases might happen)...
I use the getFirstVisible and getLastVisible AdapterView properties to detect the visible rows, and put requests in a fixed size stack.
My project is open source and has a most permissive license, if you want to use it:
https://github.com/tbiehn/Android-Adapter-Image-Loader
-Travis
I found the remote resource managing / fetching in the Foursquared source code to be pretty helpful:
http://code.google.com/p/foursquared/source/browse/main/src/com/joelapenna/foursquared/util/RemoteResourceManager.java
It caches images on disk and handles all 3 of your feature requests. See an adapter for how to use it.
As for canceling a download when a row goes off screen you'll have to handle that yourself

Categories

Resources