I'm currently experiencing some scrolling issues in a few of my views. The performance on the Galaxy Tab (7 inch) is rather poor. Rather than trying to explain my view hierarchy here, I was wondering if anyone had any general guidance on how to troubleshoot scroll performance issues on Android. I'm interested in any approaches or gotchas that I should be aware of when building views, and how to find the trouble spots that's causing the laggy scrolling
I these few suggestions would help you,
1- Use an efficient ListAdapter (taking advantage of the ConvertView in getView() method) as such:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/List14.html
2- If you are loading images over the net, thread your loader and set images to views asynchronously.
3- And if you're rendering images in your list items, keep a cache in the form of an HashMap; i.e. HashMap where integer would be the position and Drawable would be the materialized images. Use the Drawables availabe in the Cache in your getView() method and only load images if the Drawable is null when you ask for a particular index/position.
As you may already know, list items are rendered (getView() is called) every time the the item at the index is scrolled to the visible screen; so caching images and using the convertView (the view already been created once) would make your adapter and list efficient.
You could even put some system.outs in your getView() method to see the difference.
I hope these help
Best
-serkan
Related
I have a Custom Adapter for the ListView. The Layout has three images, some text. When loading the Listview, it takes a while, because of the images. Its something like Posts.
Same app in iOS is loading very fast, I think that UITableView works different than Listview.
Is there a way, in place to load all posts, only load for example 3 posts and when the user scrolls the Listview down, load the next 3, scroll down, load the next 3 and so on. This could give a better performance.
Normally, android listview work that way. Let's say there are 5 views that user can reach at the moment. ListView creates 9 views and when user scrolls it loads the bottom ones. You can think it this way. Your main problem is how are you loading your images and create the custom view. There is a common pattern for custom adapters which handles the fast recycling views(ViewHolder pattern). You should checkout the link for ViewHolder pattern. https://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html
It probably loads slow because the images should be resized every time, if you could save thumbnails it would go much faster.
Have you tried to use Recycler View instead? As long as I know using the Recycler View is the best practice nowadays. You can find a very good tutorial here:
http://www.vogella.com/tutorials/AndroidRecyclerView/article.html
You use the Recycler View almost the same way you do with List View: adapter, viewHolder, etc. It's good to mention though that you need to pay special attention to the use of the LayoutManager since the Recycler View it itself doesn't "know how to draw" the stuff on the screen.
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
I have a listview with viewpager(loads images asynchronously) in each list item. Since listview recycles images on scroll viewpager instantiates again as item goes off the screen. I have also considered using LinearLayout with adapter to populate instead of listview to prevent views recycling, but im not sure it could hold large data lists. any help??
You are clear, and what is more clear is that your architecture is terribly wrong…
The reason why the views get recycled is not because Android wants you to do extra work, it's because on a Mobile device, memory is LIMITED. If something is not visible, it should go away (most of the time).
You can increase the ViewPager offset a little bit more if you want something to stay for longer but the truth is that your image loading library should cache the images to the point where the next time you recreate them, they are instantly available, if there's memory to keep the cache alive or they should be simply re-downloaded. Welcome to Mobile Development.
Take a look at Picasso for example (on how to load images and cache them the right way).
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 scrollview in which I have inflated the view dynamically (same as facebook wall of android app, there is an imageview and textview in it.).
All the images are dynamic. If I load too much data in it then I get outofmemory exception. I know that listview will solve my problem but I cannot use the listview.
So, is there any way to optimize i.e recycle the views when they are not visible?
By your comments, I believe you still can use a list view:
A listview can have many different kinds of view. For more information on this, read Android ListView with different layouts for each row.
This way, views will be recycled and hopefully your memory usage will be smaller.
Anyway, I would probably investigate your code as I guess that for running out of memory, you probably are leaking objects or doing something way too many times than needed.
you can not handle Errors in normal circumstance but you can use following techneques to remove thias errror
1-Please clear your Arraylist before item add .
2-Use garbage collection techniques
System.gc() or finalize method use for Memory reclaim from object.