When overriding the baseadapter on an android listview, you have to implement this method public View getView(int position, View convertView, ViewGroup parent). The convertview is the view that was previously pushed off the list when scrolling, and it's given so that you can reuse that view instead of creating a new view.
My question is, is it really necessary to reuse the view? I can understand reusing it if only a piece of the data is changed. But is the overhead of creating a view really THAT significant? Every tutorial on using listviews I've seen tells you to recycle the view, even on trivially simple views like a textview.
I guess my question is why did google decide to make this the default behavior of the getView method?
A couple of reasons to recycle views:
Object creation is relatively expensive. Every additional object that is created needs to be dealt with by the garbage collection system, and at least temporarily increases your memory footprint
This is more important for more complex views, but inflating and laying out the view objects can be expensive. Most often, you are only making minor changes to the view in getView that won't affect the layout (e.g, setting text) so you might be able to avoid the layout overhead.
Remember that Android is designed to be run in a resource constrained environment.
Finally, its already done for you, and it certianly doesn't hurt anything, so why not use it?
Is it necessary? Only if you like an extra 30-40 fps during flings on a Nexus One. :) (See the slides from http://code.google.com/events/io/2010/sessions/world-of-listview-android.html, slides 13-17)
Why make the device do work that it doesn't need to do by ignoring a significant optimization that's been 95% done for you?
Related
Can I cache a view that takes a while to load?
It is a subclass of a library that is used to create a map. This view takes quite a while to load, but I don't know where this performance issue is exactly.
I use this view in a fragment across multiple activities. I want to simplify my code by caching this view, right after it is inflated, in a sort of makeshift after-the-constructor singleton, which I can check for null each time I try to load this view.
Now I have the problem that this view is inflated each time, so now I have the problem that I can't really add this to the view hierarchy.
View doesn't seem to have an addView(view) method, which I can use.
Any ideas to solve this?
In the article Multithreading For Performance from Android Developer Blog, convertView is used in Adapter.getItem() to display a list of ImageView, which are downloaded through HttpRequest.Yet, I also see some Android tutorials that don't use the convertView, and just inflate a new view in Adapter.getItem().
I'm wondering what's the advantage of using convertView? How does it recycle the used view? Where are the used view cached?
I ask this question because I didn't use convertView in my project and I want to know the cost for not using it. Below is my implementation of a listView of images.
Instead of using convertView, I inflate a new view in Adapter.getItem(). Besides, I create a wrapper class to hold the staff in each item of listView. Once the image is downloaded, it will be stored as bitmap in the wrapper class for each list item(The image is small). In this way, I can avoid the duplicate downloading of the same image. And this also avoid the race condition issues talked in Multithreading For Performance. But I'm still a little worried, is there any issues that not good by using my method?
FYI: the article recommended by #Carl Anderson gives details about how convertView works in adapter. The Google IO by Romain Guy that is suggested in this article is another good reference.
In a word, using convertView is both space and time optimized. Besides, I've abandoned my own imageDownloader and use the Picasso that is recommended by #CommonsWare. It works like a charm.
The problem with not re-using the convertView is that, as you said, you are creating and inflating a new View each and every time the user scrolls a row into view. Creating and inflating a view is a huge amount of time compared to reusing existing views, and you are certainly paying a performance hit for it.
My app does something similar - it uses ImageViews inside of rows in a ListView, and those ImageViews are populated by images that are downloaded in the background. As part of my investigation into a threading bug, I turned off reuse of Views, and the performance was absolutely terrible when I did that. ListView code is optimized for view reuse, and if you don't hold up to that, your framerate and usability will suffer.
Reading this link also helped me understand a lot better how ListViews work:
http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
I also see some Android tutorials that don't use the convertView, and just inflate a new view in Adapter.getItem().
That is not a good sign.
I'm wondering what's the advantage of using convertView?
It saves CPU time and generates less garbage in the heap.
How does it recycle the used view?
It is the used view.
Where are the used view cached?
In AdapterView.
I ask this question because I didn't use convertView in my project and I want to know the cost for not using it.
Well, depending on how you wrote your adapter, you might be getting row recycling "for free" from your superclass.
Below is my implementation of a listView of images.
There is no code in your question.
Instead of using convertView, I inflate a new view in Adapter.getItem().
Only do that if convertView is null. Otherwise, use convertView. This takes one if/else construct.
Once the image is downloaded, it will be stored as bitmap in the wrapper class for each list item(The image is small). In this way, I can avoid the duplicate downloading of the same image.
I do not quite understand where/how you are downloading the image. There are many, many libraries that do this for you, and using one is usually a better idea than is rolling your own code. I like Picasso and Ion for this, but there are others.
But I'm still a little worried, is there any issues that not good by using my method?
There may be more than what I have listed, but without seeing any code, it is difficult to guess what may be not good.
The convertView item is just a helper view, although a very important one, that helps you create less Views.
ConvertView is an optimization that has less to do with image downloading, than with regular view creation. Inflating views in android is an expensive operation, and if your list has many items, and particularly more items that can fit on one screen, the device will have to create several almost identical views to populate your listView.
Instead when you use convertView you help the system create less views, because it gives you the ability to reuse views that the system already created but that are not visible to the user, so you just need to change its contents instead of creating a new view for every item.
Not using convertView is a common cause for lag when scrolling your listviews
Using convertView in the most basic way is simply for recycling views rather than creating new views each time the view is needed, hence if your listview never losses view you might not need to implement it eg a short listview of about 3-4 views. But it is good practice check this google I/o presentation
Background
I'm using the PinterestLikeAdapterView library to show some images from the internet, which is like a gridView but with different height for each cell.
The problem
Since I use this library to show images from the internet, it's crucial that when calling notifyDatasetChanged won't cause a mess on the views.
For some reason, calling this function would call the getView() method with different positions for the views. for example, even though i didn't scroll at all, and call notifyDatasetChanged (or addAll in case it's an ArrayAdapter), for position 0 it will take what was the view of position 8, for position 1 it will take the view of position 7 , and so on...
This makes the whole grid to refresh its images, and so it ruins the UX.
Usually, in both gridView and listView, the way to overcome refreshing is to put the position that was used for the view inside the viewHolder, and if they are equal, it means that they still match.
for example:
... getView(...)
{
//<=inflate a new view if needed
//avoid refreshing view in case it's still the same position:
if(position==holder.position)
return rootView;
holder.position=position;
//<=update the view according to its data
...
}
However, here they re-use other views in a different order so this trick won't work here.
Because of this issue, not only i get refreshes of almost all of the visible views, but since i use DiskCacheLru library, it crashes since it tries to put 2 identical inputSteam data into the same key using 2 threads.
The question
What can I do?
Is this a known bug in the library?
Maybe I'm using a bad way to overcome refreshes?
for now, i use memory cache to at least get items that were cached before, but that's more like a "cure" than a "vaccine"...
Short answer:
Use an image loading library like Picasso that caches most recently used images in memory, so they don't need to be reloaded from the network.
Long answer:
AdapterView does something called View recycling, where Views which are no longer needed to display a position are re-used to display another. (For example, as you scroll down, Views that disappear off the top of the screen are reused for new positions at the bottom of the screen.) Because of this, it's normal for getView() to be passed the same View for more than one position.
This is done for performance reasons: Inflating new Views is hard and takes time, so AdapterView tries to do it as infrequently as possible.
When using a holder, you store references to ImageView and TextView children inside the item's View, so you don't have to look them up with findViewById() each time - you don't usually store anything specific to a particular position, because the View and its holder will often be used for different positions.
Now, when you call notifyDataSetChanged(), AdapterView assumes that the data set has completely changed. The image that was associated with position 8 may no longer be present, or it may be associated with position 12 now. Consequently, all the existing Views are scrapped - but because AdapterView would still like to avoid inflating new Views, they're re-used to display the new data, with no regard for what position they were displaying previously.
This explains why getView() is being passed the same View for different positions, and why visible positions are being refreshed when you call notifyDataSetChanged(). But how to avoid having your images refresh, ruining the user experience?
Use an image loading library like Picasso that caches most recently used images in memory, so they don't need to be reloaded from the network. The refresh will still happen, but it'll be instantaneous.
View getView(int position, View view, ViewGroup parent) will be always called ascendingly, after notifyDataSetChanged().
I guess that, the order of finishing download task will cause this problem.
As you mentioned in your question, keeping the position is a good way to avoid this problem.
Here is another way to solve it, also re-use the imageviews.
Keep a weak reference of each ImageView in download task.
Then wrap the download task in a dummy ColorDrawable.
When getView is called, set the dummy ColorDrawable to ImageView, and start the download. When download is complete, set the downloaded image back to the referenced ImageView in OnPostExecute().
Explanation
http://android-developers.blogspot.jp/2010/07/multithreading-for-performance.html
Source code
https://code.google.com/p/android-imagedownloader/source/checkout
There is a very good example on PinterestLikeListView in GitHub
Here is the library StaggeredGridView
A modified version of Android's experimental StaggeredGridView. Includes own OnItemClickListener and OnItemLongClickListener, selector, and fixed position restore.
You can get library project here library
and you can get Demo project Here
This is very good open source project, so you can use instead of PinterestLikeAdapterView
Hope this library is going to help you out.
seems that the authors of this library have fixed it, after some time i've reported about it:
https://github.com/huewu/PinterestLikeAdapterView/issues/8
I have a an Android ListView that has small (say, 1-5 frame) stutters as it is scrolling, about every second or so. I realize that many Android phones have these problems with animation smoothness, however, on my phone (Motorola A855 running Android 2.2), the native contact list scrolls quite smoothly. The item view in the contact list is more complex than the item view in my list, which is:
<RelativeLayout>
<TextView />
<TextView />
</RelativeLayout>
I only want to achieve smoothness as good as the native contact list. It seems like this should be possible without optimizing in native code, but perhaps I'm wrong about that.
I have tried a few things: I simplified the item view even further, and I tried instantiating it programmatically instead of in XML. I also tried changing the way I react to item click events, as per this link:
http://groups.google.com/group/android-developers/browse_thread/thread/7dc261a6b382ea74?pli=1
None of these things seem to have any effect on performance.
Is there anything I can do in my app to improve performance? I'm looking to deploy this app to a number of phones, so changing settings on the phone or rooting the device is not an option in my case. Here is the getView method from my adapter class:
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater flater = (LayoutInflater)context.getSystemService(ListActivity.LAYOUT_INFLATER_SERVICE);
layout = flater.inflate(R.layout.song_view, parent, false);
TextView first = (TextView)layout.findViewById(R.id.firstLine);
TextView second = (TextView)layout.findViewById(R.id.secondLine);
Thing t = array.get(position);
first.setText(t.title);
second.setText(t.name);
return layout;
}
Thanks in advance!
It's difficult to know why this may be happening without seeing your code.
However, one place to look is your getView() method in your ListAdapter (assuming you're using a custom adapter). Try and re-use the View that's passed as an argument to this method rather than creating a new one each time. Also don't do anything too heavy in this method (e.g. a network call), in fact try to make it as lean and mean as possible for best performance.
Did you tried Efficient Adapter
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/List14.html
From my perspective, Problem of animation smoothness is due to garbage collector running in background. If you are creating lots of object then you will see lag in listview scroll.
Hope this help.
I am building a complex view based on dynamic data. Depending on the number of data elements in collections I am adding more views. Each of these subviews are complex and get inflated in the loop through the data collection.
This is of course inefficient and I would like to figure out a way to inflate the subview only once and then reusing them instead. Is this possible somehow?
PS: I do not want to build up the subviews in code (I know I could) because that would make things even messier due to the complexities and number of subviews, but if the performance would increase considerably I might take a look at that.
PPS: There is no visible performance problem but traceview that most of the time is spent inflating and if I can make it faster I would love to ;-)
you can check out the Google IO Session entitled 'The world of ListView'.
It explains very nicely how to prevent inflating the same view again and again, and how to reuse a particular view if it has been already inflated earlier.
Here is the link.
http://www.google.com/events/io/2010/sessions/world-of-listview-android.html
You can either download the .pdf file or view the video.
Hope it helps.
Regards,
Mahendra Liya.