1

I am using a simple static image gallery in which i am loading atleast 28 images each of different size below 150kb.

and i noticed two issues here:

1) Slow scroll 2) Out of memory exception when i go to this activity two three - times.

Any one guide me what is the issue here and how to resolve it?

any help would be appreciated

Log Cat:

03-15 15:32:55.899: E/AndroidRuntime(14662): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:563)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:439)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.content.res.Resources.loadDrawable(Resources.java:1709)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.content.res.Resources.getDrawable(Resources.java:581)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.widget.ImageView.resolveUri(ImageView.java:501)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.widget.ImageView.setImageResource(ImageView.java:280)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at com.tackfu.Art$ImageAdapter.getView(Art.java:84)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.widget.AbsListView.obtainView(AbsListView.java:1515)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.widget.GridView.makeAndAddView(GridView.java:1269)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.widget.GridView.makeRow(GridView.java:315)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.widget.GridView.fillDown(GridView.java:268)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.widget.GridView.fillGap(GridView.java:235)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.widget.AbsListView.trackMotionScroll(AbsListView.java:4063)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.widget.AbsListView.onTouchEvent(AbsListView.java:2471)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.view.View.dispatchTouchEvent(View.java:3885)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:903)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:942)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:942)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:942)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1750)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1135)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.app.Activity.dispatchTouchEvent(Activity.java:2096)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1734)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.view.ViewRoot.deliverPointerEvent(ViewRoot.java:2216)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.view.ViewRoot.handleMessage(ViewRoot.java:1887)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.os.Handler.dispatchMessage(Handler.java:99)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.os.Looper.loop(Looper.java:130)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at android.app.ActivityThread.main(ActivityThread.java:3687)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at java.lang.reflect.Method.invokeNative(Native Method)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at java.lang.reflect.Method.invoke(Method.java:507)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
03-15 15:32:55.899: E/AndroidRuntime(14662):    at dalvik.system.NativeStart.main(Native Method)

Activity Code:

 @Override
        public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
            setContentView(R.layout.art);

 GridView gridview = (GridView) findViewById(R.id.gridv
 gridview.setAdapter(new ImageAdapter(this));

}

 public class ImageAdapter extends BaseAdapter {
            private Context mContext;

            public ImageAdapter(Context c) {
                mContext = c;
            }

            public int getCount() {
                return mThumbIds.length;
            }

            public Object getItem(int position) {
                return null;
            }

            public long getItemId(int position) {
                return 0;
            }

            // create a new ImageView for each item referenced by the Adapter
            public View getView(int position, View convertView, ViewGroup parent) {
                ImageView imageView;
                if (convertView == null) {  // if it's not recycled, initialize some attributes
                    imageView = new ImageView(mContext);
                    imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
                    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                    imageView.setPadding(8, 8, 8, 8);
                } else {
                    imageView = (ImageView) convertView;
                }

                imageView.setImageResource(mThumbIds[position]);
                return imageView;
            }

            // references to our images
            private Integer[] mThumbIds = {
                    R.drawable.i1, R.drawable.i2,
                    R.drawable.i3, R.drawable.i4,
                    R.drawable.i5, R.drawable.i6,
                    R.drawable.i7, R.drawable.i8,
                    R.drawable.i9, R.drawable.i10,
                    R.drawable.i11, R.drawable.i12,
                    R.drawable.i13,R.drawable.i14,
                    R.drawable.i15,R.drawable.i16,
                    R.drawable.i17,R.drawable.i18,
                    R.drawable.i19,R.drawable.i20,
                    R.drawable.i21, R.drawable.i22,
                    R.drawable.i23, R.drawable.i24,
                    R.drawable.i26, R.drawable.i28,
                    R.drawable.i29, R.drawable.i30
            };
        }
UMAR-MOBITSOLUTIONS
  • 77,236
  • 95
  • 209
  • 278

3 Answers3

1

28 images with 150KB size is about ~4MB RAM. Plus application data and it doesn't fit into small heap. You may set large heap into manifest:

android:largeHeap="true"

But this method is look as workaround, because you have unlimited memory and increasing number of images could couse again this error. You also should use lazy loading or resize images by device screen size.

neworld
  • 7,757
  • 3
  • 39
  • 61
  • http://www.youtube.com/watch?v=_CruQY55HOk. In the video, the guys gives a warning about android:largeHeap="true" saying just because a need to large heap do no use this. More the heap size more frequently garbabge collection takes place. – Raghunandan Mar 15 '13 at 10:52
  • 1
    You are right, large heap is not solution. Thanks for the link. – neworld Mar 15 '13 at 11:10
  • Best answer! I had a similar problem (trying to allocate ~5 MB for a dozen of images) and could not solve it for hours. This parameter is what I needed. – Alexey Jun 24 '16 at 10:50
1

Recycle bitmaps when not in use. Use a view holder for better performance. http://www.youtube.com/watch?v=wDBM6wVEO70. I suggest you have a look at the link. Whatever applies to listview also applies to gridview and the video talks about it. The video also has a talk on view holder which is exactly what you should be using to avoid memory leaks.

There is also a talk about avoiding memory leaks at http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html.

http://developer.android.com/training/displaying-bitmaps/load-bitmap.html. see the section Load a Scaled Down Version into Memory

Use a MAT Analyzer to check memory leaks. http://www.youtube.com/watch?v=_CruQY55HOk.

Consider some tips at http://developer.android.com/training/articles/perf-tips.html.

Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • thank you raghunandan, you have the detailed answer and it helped me alot to resolve this issue. – UMAR-MOBITSOLUTIONS Mar 15 '13 at 11:07
  • glad it helps. you are display 28 images. Some apps have more. So displaying 28 images should not give you out of memory error. If you load them efficiently you should not run out of memory. – Raghunandan Mar 15 '13 at 11:10
0

The reason of slow scroll is quite simple. You're loading images in UI thread each time a view is created. You should delegate loading to another thread with low priority. If you wish, you can load thumbnails in UI thread to quickly show something, then load full resolution in background.

Keep in mind that Android has a lot of neat tricks to make your app nice, but you have to use them wisely. For example Android caches static views in so called Drawing Cache, but in case of ImgaeViews it's pointless, because it takes additional memory and gives nothing. Using hardware accelerated UI uses even more memory, because each view cache is drawn on power-of-two texuture. It means that 100x35px button will take 128x64x4 bytes of additional memory. Android will also try to use some kind of antialiasing, like linear texture sampler - it's nice, but takes a lot of CPU time and you may want to turn it off.

Loading images via setImageResource gives you very little control. You may want to load images by yourself so you can control loading and unloading more precisely. You can also cache images a bit. LRU cache is a good example of a class made for such use: http://developer.android.com/reference/android/util/LruCache.html . You can load current image and preload one on the left and one on the right. When a user would go back to the previous image, you will have it in memory.

Also, if you wish to use brute force, you can cache your images using NativeBuffer: http://code.google.com/p/native-buffer/ . It will force you to redesign your gallery, but it can hasten image-heavy app a lot.

Zielony
  • 16,239
  • 6
  • 34
  • 39