Scrollable gallery in android, deallocate memory - java

So, I currently have a recycler view with gridlayoutmanager which through a listener loads more bitmaps asynchronously. Problem is, when I scroll too far down, program crashes due to the fact that there's an ArrayList holding 1000+ bitmaps.
I tried to compress bitmaps with an inSampleSize of 8, and to load from the thumbnails, so my guess it that problem resides in the array holding so many bitmaps.
My question is, is there any way I can, say after 200 bitmaps start to delete them from the array without breaking the scroll system? After deleting them, how would I place the Cursor to start getting bitmaps again? I can add a function that after 200 bitmaps gets called, but I need some hints in order to implement it.
I guess this is a very code-independent question, but in case any code is needed you can have a look at this repository.
Thanks.

Related

Recyclerview onCreateViewHolder being called despite all views being recycled?

Short Story:
I have a (pre-caching)Custom LinearLayoutManager, RecyclerView, Custom RecyclerView.Adaptor, Custom RecyclerView.ViewHolder setup. I have only one viewType and lightweight binding functions. Its really nothing super special which is why I hoping I don't need to post code. I also don't want to confuse with all the irrelevant code.
The issue I have is that despite not failing to recycle any views, onCreateViewHolder is still being called occasionally (after initial row inflations) leaving me wondering that maybe I have a memory leak? Do you think this is the case and why? What is deciding that my app still needs to create more views instead of recycling?
I will add one thing that maybe factoring in somehow. My rows have two visual states (they expand and collapse) and it kinda appears that having a more random mix of rows in different states worsens the problem.
Full Story:
Ive noticed an occasional hiccup in the smooth scrolling of my RecyclerView. Using android studio's profiler, I have noted the following:
All my bindViewHolder methods are plenty fast and are not holding back the scrolling.
OnCreateViewHolder is what is causing the stuttering. This explains why there is always a few stutters during the first scroll. Moreover, its the inflation thats taking a ridiculously high percentage of the cpu time.
With an item/row layout constructed using ConstraintLayout, the onMeasure functions' poor performance, destroyed the scrolling performance on weaker devices.
With an item/row layout constructed using LinearLayouts, the performance improved drastically. However, the inflation of views still takes long enough to cause hickups.
With this information, I have simplified my row item's layout as much as possible making sure to use LinearLayouts. Irregardless, the rendering of the recyclerview's items shouldnt result in stuttering after the first items come on screen because one, the rows are all the same except for the data binded to them and two, the RecyclerView is supposed to recycle the rows. So onCreateViewHolder should be called a lot initially and then rarely called again. What about pre-caching? I have found this to be one reason that new viewholders are requested when scrolling. I set the cache and created a custom LinearLayoutManager that overrides the pre-caching (pre-fetching?) method called getExtraLayoutSpace(RecyclerView.State state) and adjusted the two so that there are enough existing recyclable views to cover the request during scroll. My tests confirm that after initial scroll, new views arent requested when transitioning into scrolling state.
All that and I have two issues remaining. One of them being that onCreateViewHolder is called every so often during the use of the app and this causes a little hiccup. I put a Log.w() inside onFailedToRecycleView() to see any views are not being recycled and it looks like views are being recycled. So now I think there is some memory leak and the memory profiler shows jumps in memory usage often occuring when onCreateViewHolder is called.
The three questions I posted where,
Do I have a memory leak because Recyclerview keeps creating Viewholders?
Why do I have a memory leak?
What is deciding that my app still needs to create more views instead of recycling?
I believe I have essentially found the solution to the 2 out of 3 of the questions or the respective underlying problems.
Inside Recyclerview class is a Recycler class and RecycledViewPool class. The internal Recycler object is what makes a request to the RecycledViewPool object for an available detached scrap viewholder to recycle if it doesnt already have an attached scrap viewholder to recycle. And specifically, this search for a recyclable viewholder happens inside Recycler's getViewForPosition(int position) function. If (inside this function), a recyclable view is not found, then mAdapter.createViewHolder(RecyclerView.this, type) is called. This leads to onCreateViewholder being called. So to answer the 3rd question, Recycler is deciding when to call onCreateViewholder.
Note that the RecycledViewPool's job is to maintain an array of arraylists of detached scrap viewholders (1d array of an arraylist of each viewType). The maintaining functions include clear() , reset(), and setMaxScrap(int viewType, int max) which do what their names indicate. So the pathway to the solution to second question is to create a custom RecycledViewPool that indicates when scraplist is cleared or diminished so I can track the creation and destruction of scrap views. As far as question 1, this can be solved by using setMaxScrap(int viewType, int max). Also, on initialization of the recyclerview we can put a few extra scrapviews in the pool and try to maintain a minimum number of scrapviews which can probably be done with a listener.

OutOfMemoryError Android Issue

There's three screens. The first is the main screen, the second is a filler screen and the third contains a screen with 4 buttons.
Main screen contains one button that leads to the 2nd screen, 2nd screen contains another button that leads to the 3rd screen.
Images are stored in drawable-hdpi.
The outofmemoryerror occurs when I press the button in the 2nd screen, specifically the android.app.Activity.setContentView in the onCreate on the 3rd screen.
The app works fine if it's only going from the 1st to the 3rd screen but we need to add some screens in between the 1st and 3rd screen.
Here's the logcat:
Edit: I have a S4 (International) but it works fine on a Nexus 5.
It looks like you are loading a bitmap into memory. In an android app you only have about 16 megabytes of ram available, so you can run out of ram very quickly, and an outOfMemoryError will occur.
You can fix this by using the resource id of the image instead of a Bitmap object. For example, use:
setContentView(R.layout.view_id);
where view_id is the id of the view you want displayed.
The use of images inside your activities can lead to occasional OutOfMemoryError, if you don't follow some guidelines.
First of all, take extreme attention when declaring, initializing and using Bitmaps inside you code. These objects are managed by the system in a special way, this is a good place to start if you want to use them efficiently.
Anyway, since you didn't post any code that show the use of Bitmaps, I focused the attention to a single line of your question:
Images are stored in drawable-hdpi.
Are the images stored ONLY in such directory? Well, this is a bad practice and it's a source of memory errors ;)
When you ask Android to initialize a particular Activity, all the images declared inside its XML are loaded from a particular drawable-**** folder, depending on the screen size of the current device.
For example, a mdpi device will start to look inside the drawable-mdpi for the image named img. If it's not found, then it looks with a cascade behaviour inside all of the other drawable folders.
The problem is here: if the img is found in a different folder than drawable-mdpi, first it's scaled by the system to match the scaling factor for mdpi. Hence, another copy of the Bitmap is created.
Since all your images are inside a single, high resolution folder, the scaling operation will be executed by every non-hdpi device that will run your application. This is not good at all (here some docs).
The solution is to create different scaled versions of the same image and store them in multiple folders, like: drawable-ldpi, drawable-mdpi, drawable-hdpi, drawable-xhdpi
There is a convenient tool that does the dirty job: 9 Patch Resizer

When to recycle bitmap in android project?

I have successfully implemented lazy loading of list images and list items in Android listview. I am using Android 4.0+ and Java 7.
The algorithm i followed is:
List data(including image URL) is downloaded from internet as and when user scrolls the list.
When scroll state is idle, list images are loaded.
In the background thread, images are first checked for in the cache. If not present in cache, they are downloaded and stored into the cache.
Lastly image is set to imageview in the listview and adapter is notified.
The only problem is I am not clear about when to recycle bitmaps. I tried using bitmap.recyle() at many places but I got the following error:
java.lang.IllegalArgumentException: Cannot draw recycled bitmap
It is not possible to add that vast code over here. Also there are some privacy issues. Can someone please help me about this?
EDIT
My application size increases from 727 KB (at the time of installation) to 14 MB.
After I recycle my bitmaps, in getView() of adapter I get "cannot generate texture from bitmap ".
Can anyone suggest how to get rid of it?
Recycling a bitmap renders it unusable. Only recycle when you're completely done with it. In your case, that means after it's been evicted from the cache. You'll also want to make sure that none of your existing views reference it.
As of ICS the need to recycle isn't necessary. There are a few instance where you would want to but considering most listview implementations it probably won't be necessary.
You can check out this video by Chet Hasse for more info on reusing bitmaps which would be better if they are the same size. DevBytes: Bitmap Allocation
Bitmap recycling should be performed differently in different versions of Android. It is best to implement in a way that covers the majority of versions.
As others here said, recycle() renders your bitmap unusable, recycle() is meant to be used once you are finished with the bitmap and would like to induce a garbage collection. I think you should use it on your activity onPause()/onStop().
See here for more information:
Managing Bitmap Memory

Gallery won't scroll properly when image in custom list item is loaded

Here's the situation:
I have a custom list item containing an ImageView, and some TextViews. I am lazy loading images to the ImageViews. When I use this in a ListView, it works fine.
I also want to use it in a Gallery. There are three scenarios:
lazy load images, not touching the spacing: The items overlap, but
scroll fine
don't load images, Gallery.setSpacing(0): The items are spaced
correctly, the scrolling works, but I have no images.
lazy load images, Gallery.setSpacing(0): The items are spaced
correctly, scrolling doesn't work, just behaves really jittery and
snaps back to original position. If I scroll very fast, repeatedly, it will start moving, but very poorly
Because it all works perfectly in a ListView, I can't understand what's going wrong. I know it's nothing wrong with my image loader, and I know it's nothing wrong with my layout. It seems like Gallery is having trouble handling changes to the list item.
EDIT: I forgot to mention, there's a placeholder image loaded in the ImageView before the images are loaded, and the ImageView has a fixed size.
Does anybody have any idea what I could be missing?
I think that because yo are lazy loading the images, when onMeasure is called on each item in the gallery, it doesn't know how big the image going to be (as it isn't loaded yet), and so it can't set the spacing to 0.
Maybe you should set your default gallery item size, or update the size of the view as the image is loaded?

GridView loads slow for large image resources

I've been learning android programming for some days. I am creating an ebook application which reads images from the drawable folder. I've successfully implemented the pageviewer activity which loads bitmaps using asyncTask in background and shows a text "loading..." until loading is completed.
I wanted to show a pagelist in another activity using gridview so that user can scroll through a grid of pages and select any. I followed the android gridview tutorial and wasnt much problem. But since i have about 50 images in drawable and it seems gridview adapter shows the grid once all images are croped and placed in grid. It takes a lot of time to show the grid and its quite slow on scroll.
I was wondering if there was a better way to show the grid, asynchronously, like show the first item and then the 2nd and so on, instead of waiting for a long time and displaying the grid. I saw some topics like lazy load which seems similar to what i want, but they all show image from web, it was confusing. Hope i can get some sugestions, or if there is any other way.
Have you tried Fedor's Image Lazy Loader?
Here is the link: Lazy load of images in ListView ,
I know you want to implement it for GridView, but still you can refer the ImageLoader class given in the example code.
Update:
You can even try this example Lazy Loading GridView. I haven't tried it, but as i found it, i thought it may be of your help.
The images you show are probably a bit too large to be used with GridView in their current form.
I guess you need the GridView to display thumbnails, so one possible approach would be to store a thumbnail sized copy of every page you have, and use those in the Grid.
You can also try switching hardware acceleration on in the manifest, that might help you with the scrolling once the images are small enough to load them faster. But afaik hw acceleration is only available in 3.0 and up.

Categories

Resources