Android: keep track of number of objects in cache - android

I am building a gallery using a ViewPager. Every time a photo is downloaded I immediately cache it in mMemoryCache which is
mMemoryCache = new LruCache<Integer, Bitmap>(cacheSize) {
#Override
protected int sizeOf(Integer key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number
// of items.
return (bitmap.getRowBytes() * bitmap.getHeight());
}
};
As you can see sizeOf() returns the number if bytes used, which makes sense. My problem is that my ViewPager's adapter needs to know how many pages I have, which would be the number of objects in my cache.
Any ideas on how I can do it? thanks!

I think you should rethink your approach. Usually, when you use some kind of paging (i mean data paging, not the ViewPager), you first add a certain fixed number of items to your Adapter. You can either start downloading the required resources right away, or wait until you really need them. Check in Adapter.onCreateView() if the resource is already downloaded and cached. If yes, get it from there, if not, start an asynchronous download and add the image to the view as soon as you got it. Show a placeholder in the meantime.
When you reach the last item in your adapter, add another batch to it and everything starts over again.
If you really want to know what items are in your LRUCache, override put() and entryRemoved(). There you know what items get added or evicted from the cache. I would advise against this method though.

Related

Load Items in Advance in Recyclerview

I am using Recyclerview to load contacts in my android app. Each row has a contact number Textview and a username Textview. Contacts load from local database but usernames should load from remote server. I have tried loading usernames for each contact in onBindViewHolder method but its get stuck whenever I scroll fast.
#Override
public void onBindViewHolder(final ContactsAdapter.ContactsViewHolder holder, final int position) {
final ContactInfo current = cDataset.get(position);
//Here I load from remote server...
}
So I want to load usernames in advance while scrolling like Endless recyclerview. Is it possible to implement this?
I am not sure I got exactly what you are saying, but it seems like you want to load all data before loading it to your recyclerView.
instead of loading from remote server on onBindViewHolder, you can load it in advance either in your Activity or Fragment and pass it to your adapter...
Don't put the get call in your onBindViewHolder, you should already have your data by now.
Either obtain your data via a call prior to initializing your Adapter or if you still need to get the data from inside the Adapter, put that call within a background thread, though you are likely to run into problems there as well if you scroll fast.
Let's say that you load items in chunks of size of 30. Add OnScrollListener on your RecyclerView. Every time you pass some threshold, for example, 20 out of 30 items, you initiate fetching the next chunk of 30 items, and, once you have them, put them at the and of the RecyclerView.
Play with this numbers. Instead of 30 and 20, you may try 100 and 50, or 50 and 10. Find numbers for which you have the best UX. However, do not count that user will always have prepared next set of data, so keep one circular ProgressBar at the end of RecyclerView as long as you have more data to load.
Another approach which you may try is to periodically start a service that will fetch all required data, prepare everything the way it is required for UI, and cache it in memory or in DB.

Android: GridView Adapter needs to download and display images with async tasks - too slow

I'm building an app that should make it possible to browse the images of an internet site with lots of galleries and photos. Here are the steps that the app is going through:
Extract URLs to albums and their images from rss feed
Build a ListView with every Album and one thumbnail
The ListView's adapter getView() method sets every item's ImageView to a placeholder, which will be replaced with the real thumbnail by the corresponding AsyncTask
The Adapter's getView() then executes an AsyncTask which fetches the image
The AsyncTask checks the cache first and if the picture is not there, it downloads
Using WeakReferences, the AsyncTasks update (or not) the desired View with the aquired image, thus replacing the placeholder.
When an Album is opened, I go through the same procedure as in step 2 but use a GridView instead of the ListView to display the album contents.
/* GridViewAdapter.getView(...) follows */
public View getView(int position, View convertView, ViewGroup parent)
ImageView thumbnail;
// Recycle or not...
if (convertView == null) {
// Create new View
thumbnail = new ImageView(mContext);
} else {
// Recycle View
thumbnail = (ImageView) convertView;
}
// Set the placeholder Drawable
thumbnail.setImageResource(R.drawable.placeholder);
if (position < amountOfPhotos) {
if (album.getLinks().size() >= 1) {
// imageFetcher creates an AsyncTask for every call of loadThumbnail(...)
imageFetcher.loadThumbnail(thumbnail, album.getURL(position));
}
// Formating the ImageView a little...
...
}
return thumbnail;
The problem is the performance of the AsyncTasks. The ListView displays on my phone around 7 items resulting in roughly a dozen simultaneous AsyncTasks when the user scrolls through the list. This is fine, the list still builds quickly.
Now the album's GridView displays 15 items at once and scrolling through the List creates many many AsyncTasks. Since some AsyncTasks have to download from the web, they stay alive for a couple of seconds. This completely slows out the AsyncTasks, which would only have to reload Bitmaps from cache.
The result is, that as long as many AsyncTasks are running, the GridView does not display images when scrolling back up, even if it just displayed them a few seconds ago. Simply because there are too many AsyncTasks.
Any suggestions on how to solve my problem? I was thinking of something like an AsyncTask factory, where I can queue jobs and set priorities. That way I could control which job is executed next and I could also control the maximum amount of AsyncTasks running at once.
It would already help me if someone could tell me if my approach sounds right in general, or if I'm completely on the wrong track with AsyncTasks here...
I suggest you to use a library like Picasso that's make all of your placeholder/async image downloading very, very easy !
Starting from Android 3.0 AsyncTasks are executed sequentially. So it's totally possible that some long-running AsyncTasks will block others. In order to execute the concurrently you can use asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
But generally a good solution will be to create a ThreadPoolExecutor and execute your Runnables on it. This way you can control the number of tasks running and decide which order to use.

ListView with endless scroll: how to prevent refreshing visible rows?

I've implemented OnScrollListener to understand if the user is at the end of the list and I've managed to successfully appended new data to my list as follows:
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
if (list.getLastVisiblePosition() >= list.getCount()-1) {
currentPage++;
loadMore(currentPage);
adapter.notifyDataSetChanged();
}
}
}
The problem is notifyDataSetChanged refreshes the whole ListView as expected. But since in my list there are images that are asynchronously loaded from the internet, this refresh causes the images to load again and this is obviously not desired. The applications like Facebook, Twitter etc they seamlessly attach new data at the end without this kind of refreshing at all. How do they do it, how can I also achieve this seamless endless scroll?
I think I have to do something in my adapter's getView method to prevent reload but I could not figure it out yet. Is there a known way for it?
Thanks!
You are going to want to implement an image cache. The cache should be asked for the images and either load them asynchronously from the internet or load them from memory if used before. The cache should be used in getView.
The simplest way to do a cache is to have a hashmap (probably a ConcurrentHashMap if you are getting the images async on a separate thread), where the keys are the url and the value is the image you want to show. You can then check if the hashmap has the value first, otherwise get the image and insert in the hashmap as well. There are many other ways to do this if you look around, but this is probably the easiest.
This solution is more robust, as you do not need to worry about what to invalidate and you do not need to worry about making more than one call when requesting the same image.

Android Volley - Cancel NetworkImageView request in Base Adapter

As the title says, I'm using a BaseAdapter to display items in a ListView. Obviously a ListView will reuse views, including TextViews and NetworkImageViews.
Assuming 3 items can be displayed at once, the NetworkImageView will be reused for items at index: 1, 4, 7, ....
Depending on what's being displayed, the NetworkImageView will either:
request the image from the Network and display it,
display a cached Bitmap,
or display a local drawable resource.
Items 2 and 3 work fine, however in Scenario 1, let's say we're displaying item at index 4 from the network, and the user scrolls to item 7 before 4 is loaded and it's a local resource, we display the local resource. However our network image request may just be finishing now, so we end up displaying an incorrect image.
How can I enforce the proper (expected)behavior?
The answer from #Snicolas is spot on, but lacks some pointers on how to actually accomplish that. So here goes.
The general idea is to keep track of the ongoing image requests for every row. That way, when you encounter a recycled row, you can cancel the pending request and kick off a new one for the new data relevant to that row.
One straightforward way to accomplish that is to make the ImageContainer that you can get back when requesting an image load, part of the adapter's ViewHolder/RowWrapper. If you're not using this pattern yet, you should. Plenty of examples out there, including a good I/O talk.
Once you've added the ImageContainer to your holder, make an image request and store the container that you get back. Somewhat like this:
ImageListener listener = ImageLoader.getImageListener(holder.imageview, defaultImageResId, errorImageResId);
holder.mImageContainer = ImageLoader.get(url, listener);
The next time a recycled row comes in the adapter's getView() method, you can get your holder back from it and check wether it has a ImageContainer set. One of the following 3 scenarios may apply:
There is no ImageContainer, which means you're good to go to make a new image request.
There is an ImageContainer and the url that it is loading is the same as for the new row data. In this case you don't have to do anything, since it's already loading the image you're after.
There is an ImageContainer but the url that it is loading is different from the new row data. In this case, cancel the request and make a new one for the current row data.
If you like, you can move some of this logic by having your BaseAdapter extension implement AbsListView.RecyclerListener (and set the adapter as recycler listener for the ListView or GridView). The onMovedToScrapHeap(View view) method gets passed in the view that has just been recycled, which means you can cancel any pending image requests in there.
You don't need to enforce anything if you use the provided NetworkImageView.
NetworkImageView detects when it has been recycled and cancels the request automatically.
I don't know this API but my guess is that you should cancel any pending request before recycling such a view. How you can do that I can't say.
Did you here of alternatives like :
picasso ?
robospice UI Module ?

Download many images, then set and unset them as user scrolls to and away

My goal: Download many images and allow the user to scroll though them seemingly endlessly.
My issue: I am having trouble efficiently setting and unsetting images to conserve ram as the user scrolls. I am new to android so I am sure there is a much better way then I am doing.
My current method: this is just an outline
//I have a listener setup for the scroll
onScrollChanged(ScrollViewExt scrollView, int x, int y, int oldx, int oldy){
//if the user has scrolled 1000 pix and there is currently no other updating thread running
if(((y%1000==0)||(y%1000>990))&&!updating){
updating=true;
AsyncTask<String, Void, Void> update = new UpdateViews().execute(y+"");
}
}
//updateviews essentially iterates though all my views and calls a method in each one that returns getLocationOnScreen().
// This in turn is used to determine if the view is far away enough from the scroll to have its image removed or has come back to the area and is added to an appropriate array-list as to be altered later on the UI thread
//everything is saved to a array-list and the views are updated on the onPostExecute() because they have to be done on the main thread
Now I know there are some issues with what i wrote as it is just preliminary code but is causes the ui to lag and occasionally crash. I am looking for ideas on how to do this efficiently.
Used a listview with a ton of customization

Categories

Resources