ViewPager error(VM wont let us allocate) - android

I have a problem showing images, im testing my program with Samsung Galaxy Captivate i897, so, the problem is that i want to show images, i'm doing this with a ViewPager, but the ViewPager instantiates always three images (method instantiateItem), before, showing and after images, my images are of 5MB, so the LogCat give me that error
01-29 11:35:48.656: E/dalvikvm-heap(1275): 19660800-byte external allocation too large for this process.
01-29 11:35:48.679: E/GraphicsJNI(1275): VM won't let us allocate 19660800 bytes
I want to know how to avoid to instantiate some of 3 images or how to create an imageView avoiding this problem.
Here is my adapter's code.
public class FullScreenImageViewAdapter extends PagerAdapter
{
private final Activity _activity;
private final ArrayList<String> _imagePaths;
// constructor
public FullScreenImageViewAdapter(Activity activity,
ArrayList<String> imagePaths)
{
this._activity = activity;
this._imagePaths = imagePaths;
}
#Override
public int getCount()
{
return this._imagePaths.size();
}
#Override
public boolean isViewFromObject(View view, Object object)
{
return view == ((RelativeLayout) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position)
{
LayoutInflater inflater = (LayoutInflater) _activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View viewLayout = inflater.inflate(R.layout.img_view, container, false);
TouchImageView imgDisplay = (TouchImageView) viewLayout
.findViewById(R.id.imgDisplay);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(_imagePaths.get(position),
options);
imgDisplay.setImageBitmap(bitmap);
((ViewPager) container).addView(viewLayout);
return viewLayout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object)
{
((ViewPager) container).removeView((RelativeLayout) object);
}
}

compress your image using bitmap options and insample size check this link
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Memory constraints are always hard to solve.
If subsampling is not applicable, you can try loading images on-demand by altering original ImageView (maybe ViewPager or both) to check its visibility and discard uncompressed image data if it is invisible or decoding back if it is visible. For speed you can store compressed images in memory, decoding it on-demand.

Since your images are JPEG, you could use the RGB_565 format as you dont need to display images with an alpha channel (like a png).
options.inPreferredConfig = Bitmap.Config.RGB_565;
this will cause your image to need and use less memory as each pixel is 2 bytes (instead of 4 bytes in ARGB_8888)
More info : http://developer.android.com/reference/android/graphics/Bitmap.Config.html

Related

Java.lang.outofMemory Error

I am creating an image slider, i knew this would happen somehow due to the images that i use, i have tried android:largeHeap="true" but i don't get any luck with it.
Here is my adapter code that outputs the images
public class EsquireAdapter extends PagerAdapter {
private int [] imgs = { R.drawable.conf4,R.drawable.food,R.drawable.food1,R.drawable.food2,R.drawable.conf4,R.drawable.food,R.drawable.food1,R.drawable.food2,R.drawable.escalope,};
private LayoutInflater inflater;
private Context ctxt;
public EsquireAdapter(Context ctxt) {
this.ctxt = ctxt;
}
#Override
public int getCount() {
return imgs.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
inflater = (LayoutInflater)ctxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.swipee,container,false);
ImageView img = (ImageView)v.findViewById(R.id.ImageView);
TextView tv = (TextView)v.findViewById(R.id.textView);
img.setImageResource(imgs[position]);
container.addView(v);
return v;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.invalidate();
}
}
and here is my logcat:
FATAL EXCEPTION: main
Process: com.example.moses.ngongapp, PID: 21333
java.lang.OutOfMemoryError: Failed to allocate a 144764940 byte allocation with 16777120 free bytes and 27MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
I suggest you to use a library called Glide , to use this library add the followings in your gradle file
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
}
And then create a folder named drawable-nodpi inside your res folder . Put all of your images there
And then in your instantiateItem do the following
#Override
public Object instantiateItem(ViewGroup container, int position) {
inflater = (LayoutInflater)ctxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.swipee,container,false);
ImageView img = (ImageView)v.findViewById(R.id.ImageView);
TextView tv = (TextView)v.findViewById(R.id.textView);
Glide.with(context)
.load(imgs[position])
.asBitmap()‌​.transform(new CenterCrop(context))
.diskCacheStrategy( DiskCacheStrategy.NONE )
.skipMemoryCache( true )
.int‌​o(img)
container.addView(v);
return v;
}
Let me know if it works
The reason for this to happen is because the memory provided to your app is getting used up by your images due to its large file size.
enabling Large heap won't be a solution in this case, as it just gives your app some extra memory to work with which eventually gets used up too.
Few solutions you can try here could be
Scaling your images : https://developer.android.com/training/displaying-bitmaps/load-bitmap.html
If you don't want to manually write the code for scaling your images use library like Picasso Here's a quick tutorial on Picasso
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.fit()
// call .centerInside() or .centerCrop() to avoid a stretched image
.into(imageViewFit);
The .fit() method in Picasso does the scaling of Images for you.

Android ViewPager - OutOfMemory Exception using Glide (on phones and on TV)

I'm using ViewPager in my app for showing pictures (from camera, etc. so it can be large pictures). Also I'm using Glide library for loading pictures from Uri to ImageView. This is my PagerAdapter:
public class GalleryPagerAdapter extends PagerAdapter {
private List<FileMetaData> mPhotos = null;
private Context _context;
private LayoutInflater _inflater;
public GalleryPagerAdapter(Context context, List<FileMetaData> files) {
_context = context;
mPhotos = files;
_inflater = (LayoutInflater) _context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((ImageView) object);
}
#Override
public int getCount() {
return mPhotos.size();
}
#Override
public Object instantiateItem(ViewGroup container, final int position) {
View itemView = _inflater.inflate(R.layout.photo_pager_item, container, false);
container.addView(itemView);
ImageView imageView = (ImageView) itemView.findViewById(R.id.imageViewPhotoDetail);
Glide.with(_context).load(mPhotos.get(position).getUri()).placeholder(R.drawable.placeholder_image)
/*.skipMemoryCache(true).*/.diskCacheStrategy(DiskCacheStrategy.NONE).into(imageView);
return itemView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
ImageView i = (ImageView) object;
container.removeView(i);
}
}
Layout photo_pager_item is simple:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/imageViewPhotoDetail"
android:layout_width="match_parent"
android:layout_height="match_parent" />
And setOffscreenPageLimit is set to 1 (by default). My problem is that I'm getting OutOfMemory Exception. But I don't know how to solve it. What should I change? How can I recycle images loaded by Glide? And I need to make it work also on TV (big screen).
My problem is that I'm getting OutOfMemory Exception. But I don't know
how to solve it. What should I change? How can I recycle images loaded
by Glide? And I need to make it work also on TV (big screen).
There is nothing wrong with your implementation. if you are getting OutOfMemory Exception This usually comes when your memory buffer size becomes full let say in case if you are displaying large size images in devices which are having low memory.
Possible solutions can be :
1. As you are saying you are loading the image view from the URL. Downloading 1080*1920 size of image for the device of 480*800 is not the right way to do. So, try to make several images in you backend like small(300*300), medium(500*500),large(1000*1000), original(XXX*XXX)
Afterwords, according to the device's current resolution fetch the appropriate image from url.
2. If your image are not having transparency try to use JPEG file instead of PNGs
Obviously, you will need to do some extra efforts but this would help you to minimize the 'OutOfMemory Exception'. However, after this as well there are many scenarios due to which you can face this exception.
I hope it would help you.
After profiling the an app, I found a memory leak using the above method. This worked for me using Glide and a viewpager:
Glide.with(context)
.load(imageURL))
.asBitmap()
.dontAnimate()
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
if (bitmap != null) {
imageView.setImageDrawable(bitmap);
}
}
});

Bitmap size exceeds VM Budget? [duplicate]

This question already has answers here:
High resolution Image - OutOfMemoryError
(2 answers)
Closed 8 years ago.
I want to show my images (stored in drawable folder) in gridview. There are 12 images. For that i made a ImageAdapter class. When i open my GridViewActivity my app crashes by saying "Out of Memory: Bitmap size exceeds VM Budget". Here is the code.
ImageAdapter.java
public class ImageAdapter extends BaseAdapter {
Context context;
Integer[] imageIDs = { R.drawable.lw01_04_001, R.drawable.lw01_04_002,
R.drawable.lw01_04_003, R.drawable.lw01_04_004,
R.drawable.lw01_04_005, R.drawable.lw01_04_006,
R.drawable.lw01_04_007, R.drawable.lw01_04_008,
R.drawable.lw01_04_009, R.drawable.lw01_04_010,
R.drawable.lw01_04_011, R.drawable.lw01_04_012, };
public ImageAdapter(Context ctx) {
context = ctx;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return imageIDs.length;
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(context);
imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(5, 5, 5, 5);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(imageIDs[position]);
return imageView;
}
}
GridViewActivity.java
public class GridViewActivity extends ActionBarActivity {
private Utils utils;
private ImageAdapter imageAdapter;
private GridView gridView;
private int columnWidth;
Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grid_view);
context = this;
gridView = (GridView) findViewById(R.id.grid_view);
utils = new Utils(this);
// Initilizing Grid View
InitilizeGridLayout();
imageAdapter = new ImageAdapter(context);
// setting grid view adapter
gridView.setAdapter(imageAdapter);
}
Your images are probably too large. When you give an ImageView an image resource ID, the ImageView generates a bitmap of that resource, then holds it in memory. While most modern devices are built with enough memory to easily handle multiple large images, Android allocates a very small amount of that memory to each app. This means that you need to be clever when you work with images, especially when you need to display many of them.
Here are some options:
Make sure the images in your drawable folder aren't bigger than they need to be (i.e., they should be no larger than the dimensions of the view that's displaying them). This is probably the best option, since in addition to decreasing your memory requirements, you'll also be reducing the size of your resulting APK file.
If you don't want to scale down your images in your drawable folder (for example, you might need to show them in full size if the user clicks them), scale down the images during runtime. This option will require a bit more code, but gives you the flexibility of displaying images exactly how you need to, without running into out of memory issues.
Here's a code example for option #2:
Options opts = new Options();
opts.outWidth = 85; //Based on your size requirement.
opts.outHeight = 85;
Bitmap b = BitmapFactory.decodeResource(getResources(), imageIDs[position], opts);
imageView.setImageBitmap(b);
This should get you started.
Your Bitmaps are probably too big.
I suggest you check this question: Out of Memory Error ImageView issue
this tutorial https://developer.android.com/training/displaying-bitmaps/index.html

Out Of Memory Exception with custom GridView [duplicate]

This question already has answers here:
Strange OutOfMemory issue while loading an image to a Bitmap object
(44 answers)
Closed 8 years ago.
I have this code of a custom grid view and each time I try to run it, it crashes caused by an OutOfMemoryException. I guess the solution is to resize the images in the array ...
main activity code :
public class AndroidGridLayoutActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.grid_layout);
GridView gridView = (GridView) findViewById(R.id.grid_view);
// Instance of ImageAdapter Class
gridView.setAdapter(new ImageAdapter(this));
/**
* On Click event for Single Gridview Item
* */
gridView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// Sending image id to FullScreenActivity
Intent i = new Intent(getApplicationContext(), FullImageActivity.class);
// passing array index
i.putExtra("id", position);
startActivity(i);
}
});
}
}
image adapter code :
public class ImageAdapter extends BaseAdapter {
private Context mContext;
// Keep all Images in array
public Integer[] mThumbIds = {
R.drawable.pic_1, R.drawable.pic_2,
R.drawable.pic_3, R.drawable.pic_4,
R.drawable.pic_5, R.drawable.pic_6,
R.drawable.pic_7, R.drawable.pic_8,
R.drawable.pic_9, R.drawable.pic_10,
R.drawable.pic_11, R.drawable.pic_12,
R.drawable.pic_13, R.drawable.pic_14,
R.drawable.pic_15
};
// Constructor
public ImageAdapter(Context c){
mContext = c;
}
#Override
public int getCount() {
return mThumbIds.length;
}
#Override
public Object getItem(int position) {
return mThumbIds[position];
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(mContext);
imageView.setImageResource(mThumbIds[position]);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(new GridView.LayoutParams(70, 70));
return imageView;
}
}
full screen code :
public class FullImageActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.full_image);
// get intent data
Intent i = getIntent();
// Selected image id
int position = i.getExtras().getInt("id");
ImageAdapter imageAdapter = new ImageAdapter(this);
ImageView imageView = (ImageView) findViewById(R.id.full_image_view);
imageView.setImageResource(imageAdapter.mThumbIds[position]);
}
}
any advice please??
You are attempting to allocate too much memory for image data. As you explained in the comments, your images are fairly large and they are being read as soon as the adapter is created.
Reading this into memory will consume 4 bytes per pixel, so if your images are 1024 pixels square, the math is:
1024 × 1024 × 4 = 4MB
15 images × 4 MB/image = 60MB
This likely exceeds the heap budget of 48MB.
You will likely want to improve the efficiency through which you display these images. Consider:
Only loading the image when it is shown, using GridView's mechanisms for getView();
Loading bitmaps with a sample size using BitmapFactory.
Esp for "..loading the images in a GridView:
you can use inSampleSize to load all images and replace the visible images by the original ones- meaning for the view part that are visible, dynamically load images without the inSampleSize for the BitmapFactory and its Options.
You could scale them down depending on different devices to load the images without facing a memory problem.
In your case, some phones may not exhibit the same behavior on the first run, but eventually, without handling an optimized image loading solution, app will crash.
Topic under Load a Scaled Down Version into Memory.
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Out of memory error on Android
On how to avoid them:
How to avoid an out of memory error while using bitmaps in Android
For an overview:
http://blogs.innovationm.com/android-out-of-memory-error-causes-solution-and-best-practices/
http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html
Before setting a drawable as the background for your imageview, i.e.:
iv.setBackgroundResource(R.drawable.image);
Get a scaled bitmap or try other options like inSampleSize, do check how your solution affects the quality of your image too.
For the comment:
Bitmap drawableImage=BitmapFactory.decodeResource(context.getResources(),R.drawable.image);
Bitmap bitmap = Bitmap.createScaledBitmap(drawableImage, width, height, false);
Drawable drawableScaled = new BitmapDrawable(context.getResources(), bitmap);

View Pager with Universal Image Loader Out of Memory Error

I am not really sure if a ViewPager with Universal Image Loader can/should be used as an alternate for a gallery like interface since I have run into an Out of Memory error while loading images from SD Card and viewing them in full screen mode. No matter what the number, it works all fine with a GridView but while viewing the images in the View Pager, each bitmap keeps eating up a lot of memory and after 10 or so images, it gives the out of memory error.
I have seen almost all the questions that have been posted here related to the Out of Memory Error while working with the Universal Image Loader and in each one of them, there has been a configurations error as the cause.
I dont know if I am using the wrong configurations or what but I have wasted a lot of time on it and am kind of stuck, any help/advice would be appreciated.
The configurations for the ImageLoader:
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.memoryCache(new WeakMemoryCache())
.denyCacheImageMultipleSizesInMemory()
.discCacheFileNameGenerator(new Md5FileNameGenerator())
.imageDownloader(new ExtendedImageDownloader(getApplicationContext()))
.tasksProcessingOrder(QueueProcessingType.LIFO)
// .enableLogging() // Not necessary in common
.build();
The Display Image Options are:
options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.image_for_empty_url)
.resetViewBeforeLoading()
.imageScaleType(ImageScaleType.IN_SAMPLE_INT)
.bitmapConfig(Bitmap.Config.RGB_565)
.displayer(new FadeInBitmapDisplayer(300))
.build();
I am using the example project that was given with the library but those settings wont work either, it just crashes after some time. My guess is that there is a specific callback where I have to recycle bitmaps from the views from that are not visible.
EDIT: I know its a memory leak, the views that are not visible are destroyed when they should be but the memory is not released as it should. Heres the implementation of the destroyItem callback, followed the tips given in different questions but still cant find the memory leak.
#Override
public void destroyItem(View container, int position, Object object) {
// ((ViewPager) container).removeView((View) object);
Log.d("DESTROY", "destroying view at position " + position);
View view = (View)object;
((ViewPager) container).removeView(view);
view = null;
}
It's probably not the best implementation to solve it, but it worked for me. Removing the ImageViews is not enough, so I decided to recycle bitmaps in 'destroyItem':
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
ImageView imageView = (ImageView) view.findViewById(R.id.image);
if (imageView != null) {
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
bitmap.recycle();
bitmap = null;
}
((ViewPager) container).removeView(view);
view = null;
}
This does not clean the last 3 active pages when you leave the activity, although I hope that GC takes care of them.
Try to apply next suggestions:
Use ImageScaleType.EXACTLY
Enable caching on disc (in display options).
Finally try to use .discCacheExtraOptions(maxImageWidthForDiscCache, maxImageHeightForDiscCache, CompressFormat.PNG, 0);
Just posting this because this question is coming up on Google when searching for UIL and OOP. I had OOP problems no matter what configuration, what solved all my problems were the two classes RecyclingImageView and RecyclingBitmapDrawable from this sample project.
I also used the same library and had same error. As solution, i created a sparseArray to keep photoView instances. And use it like this:
private SparseArray<PhotoView> photoViewHolder;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
photoViewHolder = new SparseArray<PhotoView>();
...
}
private class GalleryPagerAdapter extends PagerAdapter {
#Override
public View instantiateItem(ViewGroup container, int position) {
PhotoView photoView = new PhotoView(container.getContext());
ImageHolder holder = new ImageHolder();
holder.position = position;
holder.loaded = false;
photoView.setTag(holder);
photoViewHolder.put(position, photoView);
// I used LazyList loading
loader.DisplayImage(items.get(position), photoView);
// Now just add PhotoView to ViewPager and return it
container.addView(photoView, LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
return photoView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
photoViewHolder.remove(position);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
And to handle viewPager's listener:
pager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageScrollStateChanged(int position) {
}
#Override
public void onPageScrolled(int position, float arg1, int arg2) {
}
#Override
public void onPageSelected(int position) {
if(photoViewHolder.get(position) != null) {
ImageHolder holder = (ImageHolder)photoViewHolder.get(position).getTag();
// Do something...
}
}
});
Hope this helps...
I used kutothe's implementation from github issues page.
I had this problem when simply setting Uri to ImageView using: iv.setImageURI(Uri.fromFile(imgFile));
I had the same problem with Universal Image Loader, and I even looked for other Image Loaders out there, and found another good one called "Picasso", but it also had the same problem.
So what worked for me is using GestureImageView and setting gesture-image:recycle to true through XML, and load the images with the following code:
Drawable yourDrawable = null;
try {
InputStream inputStream = getActivity().getContentResolver().openInputStream(Uri.fromFile(img));
yourDrawable = Drawable.createFromStream(inputStream, Uri.fromFile(img).toString() );
inputStream.close();
} catch (FileNotFoundException e) {
yourDrawable = getResources().getDrawable(R.drawable.ic_launcher);
} catch (IOException e) {
e.printStackTrace();
}
if (yourDrawable != null)
iv.setImageDrawable(yourDrawable);
the reason it was crashing and giving OOM error is that the bitmaps aren't recycled when the image aren't displayed on the screen anymore, hence a memory leak occurs.
If there is another way to recycle the bitmap in the normal ImageView, that would be a better solution.
Hope I helped.
I know it's late, but maybe my answer will save someone's time. After hours and hours of trying to solve this issue (with almost every answer found on stack overflow) I finally solved it with Fresco image library. It'a a lib written by Facebook and it's primary goal is to use memory in efficient way. It's really great and my Out Of Memory Error disappeared. I highly recommend using it.
http://frescolib.org/

Categories

Resources