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);
}
}
});
Related
Hi I have pretty simply code my images are all compressed so no one has more than 50kb. When I start my app 1 image is loaded and memory usage is around 42mb but when I scroll down to another images every other image cause memory usage to increase up to 250mb. How to fix it? Here is my code and please I am beginner so try to explain it simple.
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class CustomSwip extends PagerAdapter {
private int[] imageResources = {R.drawable.j1, R.drawable.jg2,R.drawable.jg3, R.drawable.jg4, R.drawable.jg5, R.drawable.jg6, R.drawable.jg7, R.drawable.jg8, R.drawable.jg9, R.drawable.jg10, R.drawable.jg11, R.drawable.jg12, R.drawable.jg13, R.drawable.jg14, R.drawable.jg15, R.drawable.jg16};
private Context ctx;
private LayoutInflater layoutInflater;
public CustomSwip(Context c) {
ctx = c;
}
#Override
public int getCount() {
return imageResources.length;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
layoutInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View itemView = layoutInflater.inflate(R.layout.activity_custom_swip, container, false);
ImageView imageView = (ImageView) itemView.findViewById(R.id.swip_image_view);
imageView.setImageResource(imageResources[position]);
container.addView(itemView);
return itemView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
#Override
public boolean isViewFromObject(View view, Object object) {
return (view == object);
}
}
Universal Image Loader is a library by nostra13 which is pretty well documented and widely used and is known for the configuration setting it provides for loading of images. In it, there is an option to load the images in RGB_565 which can decrease your memory consumption by about half (have seen the results practically) and many more.
Use .bitmapConfig(Bitmap.Config.RGB_565) in display options. Bitmaps in RGB_565 consume 2 times less memory than in ARGB_8888.
And do look into WeakReferences once. It could help in calling of Garbage Collector for memory-intensive images.
According to the docs for setImageResource,
This does Bitmap reading and decoding on the UI thread, which can cause a latency hiccup. If that's a concern, consider using setImageDrawable(android.graphics.drawable.Drawable) or setImageBitmap(android.graphics.Bitmap) and BitmapFactory instead.
Or simply use an image loading library to lazy load images
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 )
.into(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.
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 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/