java.lang.OutOfMemoryError: bitmap size exceeds VM budget.
This is one phrase you'll come across frequently in android while you try to load several large bitmaps in your program.And Why?Because the heap size available to an application is limited and large bitmaps are the easiest way of exhausting it.
So how do you work around it?
Understand that mostly,it is pointless to load a bitmap in its full capacity onto a device's screen that fits on your palm.So,all you have to do is to load a scaled down version of the bitmap and everything will be well and good.But how do you scale down a bitmap for loading? Configure and use the BitmapFactory.Options class while decoding the bitmap .
Here,for simplicity,I'll be loading a bitmap on to a single ImageView.
Before we begin,take a look at BitmapFactory.Options.inJustDecodeBounds and BitmapFactory.Options.inSampleSize.
In simple words,here's what they mean:
1. BitmapFactory.Options.inJustDecodeBounds: When set to true,we will be able to measure the bitmap dimensions,without actually loading the bitmap into memory.
2.BitmapFactory.Options.inSampleSize: The value of this int allows us to load a scaled down version of the bitmap into memory. As an example,if the value set to this variable is 2, the resulting bitmap will have a width half the original width and height set to half the original height.
That is,
New width= original width/2
New Height= original height/2.
Here,we'll be dedicating a class for loading the bitmap and in the class here's what we are going to do:
1. Measure the actual height and width of the bitmap by setting inJustDecodeBounds=true;
2. Obtain an appropriate scale(which we will set to inSampleSize),using the actual height and width.
3. Load the scaled down bitmap using the inSampleSize.
Note that this article is an implementation of the guidelines given here.
As I have said,my main.xml contains only a single ImageView,for the purpose of demonstration.
Now for the code,read the comments and you will find it self explanatory.
Note that this article is an implementation of the guidelines given here.
As I have said,my main.xml contains only a single ImageView,for the purpose of demonstration.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <ImageView android:id="@+id/bitmapView" android:layout_width="100dp" android:layout_height="100dp"/> </LinearLayout>
Now for the code,read the comments and you will find it self explanatory.
package com.blog.simplebitmaploader; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Environment; import android.widget.ImageView; public class BitmapLoaderActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //path of the file to be loaded String filePath=Environment.getExternalStorageDirectory()+ "/Sunset_Beach.jpg"; setContentView(R.layout.main); ImageView bitmapView=(ImageView) findViewById(R.id.bitmapView); //load the scaled down version bitmapView.setImageBitmap(BitmapLoader.loadBitmap(filePath, 100, 100)); } } class BitmapLoader { public static int getScale(int originalWidth,int originalHeight, final int requiredWidth,final int requiredHeight) { //a scale of 1 means the original dimensions //of the image are maintained int scale=1; //calculate scale only if the height or width of //the image exceeds the required value. if((originalWidth>requiredWidth) || (originalHeight>requiredHeight)) { //calculate scale with respect to //the smaller dimension if(originalWidth<originalHeight) scale=Math.round((float)originalWidth/requiredWidth); else scale=Math.round((float)originalHeight/requiredHeight); } return scale; } public static BitmapFactory.Options getOptions(String filePath, int requiredWidth,int requiredHeight) { BitmapFactory.Options options=new BitmapFactory.Options(); //setting inJustDecodeBounds to true //ensures that we are able to measure //the dimensions of the image,without //actually allocating it memory options.inJustDecodeBounds=true; //decode the file for measurement BitmapFactory.decodeFile(filePath,options); //obtain the inSampleSize for loading a //scaled down version of the image. //options.outWidth and options.outHeight //are the measured dimensions of the //original image options.inSampleSize=getScale(options.outWidth, options.outHeight, requiredWidth, requiredHeight); //set inJustDecodeBounds to false again //so that we can now actually allocate the //bitmap some memory options.inJustDecodeBounds=false; return options; } public static Bitmap loadBitmap(String filePath, int requiredWidth,int requiredHeight){ BitmapFactory.Options options= getOptions(filePath, requiredWidth, requiredHeight); return BitmapFactory.decodeFile(filePath,options); } }
Simple,right?? The only disadvantage of this code is that everything related to the bitmap is being done in the UI thread,which could slow down your application.In my next post,we'll see how this code can be executed in a different thread.For now,adios..:):):).