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);
Related
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
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
I'm trying to make an app that takes videos and then displays the videos in a gridview. I have gotten to the point where all the video thumbnails are showing up in the gridview like they should. However, my problem is that the method I am using has a lot of lag time. It takes some time to get the gridview loaded and when I try to scroll there is always some sort of lag. This is most likely due to the fact that I'm using bit maps for every video thumbnail.
I was wondering what I could do to make this a lot quicker and smoother. My code is posted below.
class VideoAdapter extends BaseAdapter {
Context context;
ArrayList<String> videopathlist;
Bitmap bmthumb;
VideoAdapter(Context c, ArrayList<String> filepathlist){
context = c;
videopathlist = new ArrayList<String>();
this.videopathlist.addAll(filepathlist);
Log.i(TAG, "" + videopathlist);
}
#Override
public int getCount() {
return videopathlist.size();
}
#Override
public Object getItem(int position) {
return videopathlist.get(position);
}
#Override
public long getItemId(int arg0) {
return 0;
}
#Override
public View getView(int posiiton, View arg1, ViewGroup arg2) {
ImageView imageview = new ImageView(context);
bmthumb = ThumbnailUtils.createVideoThumbnail(videopathlist.get(posiiton), MediaStore.Video.Thumbnails.MINI_KIND);
if (bmthumb != null) {
Log.i(TAG1, "There is a thumbnail");
imageview.setImageBitmap(bmthumb);
} else {
Log.i(TAG1, "There is NOT a thumbnail");
}
imageview.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageview.setLayoutParams(new GridView.LayoutParams(160, 160));
return imageview;
}
Thanks for your help
2 things:
Use a ViewHolder to cache everything, as described here: Making ListView Scrolling Smooth (applies to GridViews too)
Also, use an ImageLoader to load images dynamically. There are many available or you can build your own. Here are some:
Volley
Android-Universal-Image-Loader
ImageLoader
All these loaders make use of Bitmap caches as described here: Caching Bitmaps
Also, you could potentially be actually creating the thumbnail bitmaps when returning the view. This strikes me as very expensive. I would do this as a seperate step, unrelated to displaying them. For example, kick off an AsyncTask when your activity is first created to generate any new thumbnails. Then in your adapter just query the MediaStore to get the generated thumbnails.
I have a gallery in my android application when I clicked on gallery item I want to display image and grid view. I done when i have only three images in gallery and in click it displayed correctly. But now I have seven images in gallery and each gallery item there are four images to display gallery.In this I m using array of drwawable images only.In this time I got an exception as Java.lang.OutOfMemory
I use below code
public ImageThemeAdapter(Context c, Integer[] mImageIds) {
imagesId=mImageIds;
bitmap=new Bitmap[imagesId.length];
mContext = c;
TypedArray ta=obtainStyledAttributes(R.styleable.Gallery1);
imageBackground=ta.getResourceId(R.styleable.Gallery1_android_galleryItemBackground, 1);
ta.recycle();
for (int i = 0; i <imagesId.length; i++) {
bitmap[i]=BitmapFactory.decodeResource(mContext.getResources(),imagesId[i]);
}
}
public int getCount() {
return bitmap.length;
}
public Object getItem(int position) {
return bitmap[position];
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView i = new ImageView(mContext);
i.setImageBitmap(bitmap[position]);
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setLayoutParams(new Gallery.LayoutParams(130, 120));
// i.setLayoutParams(new Gallery.LayoutParams((ScreenWidth*80)/100, android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
i.setBackgroundResource(imageBackground);
return i;
}
please provide any suggestions. Thanks in advance.
I have have a requirement like this
Please repeat the steps using Allocation Tracker(Windows -> Shows View -> Other -> Android -> Allocation Tracker) and find out the memory allocation to different objects.
The main reason could be due the layout of custom view as you are creating GridView and at runtime you will be creating thumbinals of images of gallery. we might have to create a gridview with minimum number of images available at one time.
I have got an activity that shows a grid view with different images. When clicking on one of those images I want the clicked image to be the background image of another activity.
How can I do that?
This is my activity that shows the grid view:
public class HelloGridViewActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
GridView gridView = (GridView) findViewById(R.id.gridview);
// 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) {
//WHAT SHALL I PUT HERE????
}
}
}
So, the poster didn't (originally) specify exactly whether the other activity was to be opened immediately on click, or if the clicked item's image just needed to be recorded for use later. The first problem seems more common to me, as in a GridView that shows a set of image thumbnails. The user clicks on that, and a new Activity showing just that item's information comes up. That thumbnail image maybe goes full screen. Anyway, maybe that's not what the poster had in mind, but that's my assumption (and probably somebody will eventually find this answer with such a use case in mind).
It also isn't specified how the images are stored, so I'll make assumptions that
The ImageAdapter's images are bundled resources of this app
We can make some changes to the ImageAdapter, to store some data to solve this problem
So, with that, I take a standard ImageAdapter, and add one line of code to record the integer resource ID for each ImageView, in the ImageView's tag property. There's many ways to do this, but this is the way I chose.
public class ImageAdapter extends BaseAdapter {
/* see code removed at
http://developer.android.com/resources/tutorials/views/hello-gridview.html
*/
// 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]);
// here we record the resource id of the image, for easy access later
imageView.setTag(mThumbIds[position]);
return imageView;
}
// references to our images
private Integer[] mThumbIds = {
R.drawable.pic1,
R.drawable.pic2,
R.drawable.pic3,
R.drawable.pic4,
R.drawable.pic5
};
}
Then, when the Hello activity has a grid item clicked, I retrieve the image's resource id and pass it as an Intent extra (HelloGridViewActivity.java):
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
GridView gridView = (GridView) findViewById(R.id.gridview);
// Instance of ImageAdapter Class
gridView.setAdapter(new ImageAdapter(this));
final Context activity = this;
gridView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Object tag = v.getTag();
if (tag instanceof Integer) {
// we stored a reference to the thumbnail image in the ImageView's tag property
Intent i = new Intent(activity, AnotherActivity.class);
Integer resourceId = (Integer)tag;
i.putExtra("backgroundImage", resourceId);
startActivity(i);
}
}
});
}
And finally, when the new activity (AnotherActivity) is opened, we retrieve that intent extra, and decode it as an integer resource id, and set it as the background image with a fullscreen ImageView (AnotherActivity.java):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.another);
Intent i = getIntent();
//String bgImage = i.getExtras().getString("backgroundImage");
int resId = i.getExtras().getInt("backgroundImage");
try {
ImageView background = (ImageView) findViewById(R.id.bgImage);
// Another alternative, if the intent extra stored the resource 'name',
// and not the integer resource id
//Class<?> c = R.drawable.class;
//background.setImageResource(c.getDeclaredField(bgImage).getInt(c));
background.setImageResource(resId);
} catch (Exception e) {
e.printStackTrace();
}
}
I also show above some commented out code, if for some reason you need to pass a string name of an image resource, instead of an integer resource code. The commented out code looks up the resource id for the given image name. According to the resource names used in my ImageAdapter, a sample string name to pass might be "pic1". If you do this, of course, the calling Activity (HelloGridViewActivity) needs to encode the Intent extra as a String, not an integer.
Get the resource ID of the image you want as the BG and save it to preferences.
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
saveImageId(position);
}
private void saveImageId(int position) {
int id = getImageId(); // get the R.drawable.* id of the image. You should be able to figure this out.
Editor ed = PreferenceManager.getDefaultSharedPreferences(this).edit();
ed.putInt("bg_image_id", id);
ed.commit();
}
Now in your other activity, you can get the image id:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
int id = prefs.getInt("bg_image_id", R.drawable.default_background); //Get image id, use default background if there isn't one.
LinearLayout mLayout = (LinearLayout) findViewById(R.id.background_layout);
mLayout.setBackgroundResource(id);
Good luck