I need to load lots of big images (500 .png files) from the SD card to my app. Do I always have to convert images to Bitmap and make Bitmap files? I don't want to resize the Heap.
Is there another way to read the images from SD card?
If you're displaying them in a view, then you have to load them into memory in their entirety.
You didn't mention how large your images will get, but what we do in our photo gallery is to keep a list of SoftReferences to these bitmaps, so that the garbage collector can throw them away when they're not visible (i.e. when the view displaying them gets discarded--make sure that this actually happens, e.g. by using AdapterView). Combine this with lazy loading of these bitmaps and you should be good.
The internal representation of the image in your app is a collection of bits and bytes - not an image of any specific format (png, bmp, etc).
The image is converted to this internal representation when the image is loaded by the BitmapFactory.
It is usually not a good idea to load all the bitmaps at once, you will quickly run out of memory...
If your image's dimension is very big, you must to resize them before loading in to ImageView. Otherwise, even one picture can easily cause out of memory problem. I don't know how many images you want to display concurrently and how big they are. But I suggest you to resize them before displaying them.
To resize image and show it, you can use this code:
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(fileInputStream, null, bitmapOptions);
//reduce the image size
int imageWidth = bitmapOptions.outWidth;
int imageHeight = bitmapOptions.outHeight;
int scale = 1;
while (imageWidth/scale >= screenWidth && imageHeight/scale >= screenHeight) {
imageWidth = imageWidth / 2;
imageHeight = imageHeight / 2;
scale = scale * 2;
}
//decode the image with necessary size
fileInputStream = new FileInputStream(cacheFile);
bitmapOptions.inSampleSize = scale;
bitmapOptions.inJustDecodeBounds = false;
imageBitmap = BitmapFactory.decodeStream(fileInputStream, null, bitmapOptions);
ImageView imageView = (ImageView)this.findViewById(R.id.preview);
imageView.setImageBitmap(imageBitmap);
In my android project, I am using this piece of code to resize my HD wallpaper to review it.
Android Save And Load Downloading File Locally
Related
I am creating a bitmap and it takes about 11 mb in the heap , though it is of the small size. Well I wanted to know if I can create the bitmap and also sclae it as a same time. The reason I want to do it is , the memory allocation , as If I understand correctly from different bitmap questions which are posted here , and that is
The bitmap allocates the memory as when it is created
So if its , then scaling it again take some process time and also increase the heap size until and unless the garbage collection is not occurred
So what I am doing is
screenHeight = displaymetrics.heightPixels;
screenWidth = displaymetrics.widthPixels;
float aspectRatio = screenWidth / screenHeight;
int modifiedScreenHeight = 400;
int modifiedScreenWidth = (int) (modifiedScreenHeight * aspectRatio);
mBitmap = Bitmap.createBitmap(modifiedScreenWidth, modifiedScreenHeight, Bitmap.Config.ARGB_8888);
So now it is creating the bitmap and allocation the memory , by memory analyzer tool in android studio I can see that it took 11mb in memory.
But I want to minimize them ,I have visited a link and I want to do some more scaling by options as show in this video . but it uses the file to decode such as
BitmapFactory.decodeFile(??,options);
where as I have no file to decode from , I want to decode it from the bitmap I created and to wash away the last created bitmap to clear the memory.
Or if it is possible to set the options when creating it so that we can avoid from extra memory allocation .
Please help.
You can use this using BitmapFactory.Options - specifically, use the options to decode the width / height of the bitmap, then sampleSize to determine how large the generated bitmap will be.
According to your example, you'd like the width/height of the bitmap to be 400 by 400 * aspectRatio. So, first, you'll need to see how large the bitmap needs to be. Do this as so:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(??, options);
int bitmapWidth = options.outWidth;
int bitmapHeight = options.outHeight;
This action will only decode the bitmaps size, without actually allocating memory for the bitmap's pixels. This is good because it's a very quick and light operation which doesn't require much resources and helps you make a more educated decision when loading the bitmap. Now we must use these size to determine how big the generated bitmap will be.
int sampleSize = 1;
while (bitmapWidth / sampleSize > 400 && bitmapHieght / sampleSize > 400 * aspectRatio)
sampleSize *= 2;
sampleSize must be a power of 2 for this to work, and what it will do is determine how many pixels to "skip" when reading the bitmap into memory. This algorithm will set a sample size to a size equal to 1st sample size which will produce a bitmap immediately smaller than the required bounds. You can tweak this if you'd like a slightly different implementation.
Now that you have the sample size, set it with in the options object and load the actual bitmap:
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
Bitmap bitmap = BitmapFactory.decodeFile(??, options);
The generated bitmap will be smaller than the required bounds, thus limiting your memory requirements for creating the bitmap object.
Hope this helps.
So, I'm updating an old e-book reader. The problem I'm facing is the size of the bitmaps (the pages). The way it works is: the app downloads a file, extract it, decode it with our DRM then I can access all pages. The thing is, each page its a 35mb bitmap.
I need to reduce this by a lot. I can't convert the file to any other format.
I tried this:
byte[] rawBytes = intToByteArray(b.getByteCount());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bit = BitmapFactory.decodeByteArray(rawBytes, 0,rawBytes.length, options);
But the Bitmap keeps returning null, and for what I gathered its when the Bitmap facorty can't decode the bitmap.
So, there is any other way to reduce the impact of this big bitmaps ?
I'm developing application for my android tablet that using many images.
in my activity, I use effect splash that show image (3840 x 2108 70 KB) for the opening theme.
I use another background image (3840 x 2108 69 KB).
but, when I test it on my tablet, this application's cache reached 80 MB!
For your information, I load the image from my xml file.
Can anyone help me to reduce it?
Is it wrong to load image from xml file?
Is it the size of my images that cause this problem?
//UPDATE
at the end, i didn't found a really good way to my question. I'm using xml (I put at dawable) file to replace my big size image, and this method really reduce the cache alot. My conclusion is avoid using big image, instead, just replace the color image using color.
The advantage to use color is "faste to load", it reduce lag of my application.
Android default color model is ARGB_8888. It takes 4 byte for 1 pixel. So splash and background bitmap images in memory takes 3840*2108*4*2 = 61.76MB. You could resize the images for different device dpi and put them in proper drawable folder. For example, drawable-hdpi is suitable for 240 dpi, drawable-xxhdpi is suitable for 480 dpi.
In addition, you could manually load the images with java code. The following is a method to resolve the image safely:
protected Bitmap getBitmapSafely(Resources res, int id, int sampleSize) {
// res = youractivity.getResources, id = R.drawable.yourimageid
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inSampleSize = sampleSize;
try {
bitmap = BitmapFactory.decodeResource(res,
id, options);
} catch (OutOfMemoryError oom) {
Log.w("ImageView", "OOM with sampleSize " + sampleSize, oom);
System.gc();
bitmap = getBitmapSafely(res, id, sampleSize + 1);
}
return bitmap;
}
Your could use this method with sampleSize = 1 at first time. If there is not enough memory, it will catch OOM and increase the sampleSize until the image can be resolved.
I want to show a preview of some images from the users device. For this purpose I search the external storage for image files and for every folder which contains some, I list the folder's name and an image from this folder in a listview.
On my Nexus there are 6 folders containing images, so I have 6 listview items.
I load all images using:
Drawable.createFromPath(file.getAbsolutePath())
And cache the resulting drawable in a HashMap in order to prevent loading the same image multiple times.
However, the heap is growing from 20MB to >90MB. When the images are loaded the app response is delayed like 2 seconds. Pretty bad.
I have no idea how the heap can grow to 90MB from 6 images which are like 50KB but whatever. To fix this I tried to load subsampled bitmaps from the images - however whenever I load them I get an outofmemory exception.
I have verified multiple times that not more than those 6 images are loaded.
What can I do?
What you should do is analyze your app's memory usage using MAT tool, as described in this amazing article. This tool will help you identify potential memory leaks and see what exactly triggers the heap growth.
I have used the tool what Egor described and there were no memory leaks. The images took about 30MB heap. This amount can only be reduced by reducing the image size. My solution uses subsampling and takes about 3MB heap now.
private Drawable loadDrawable(Context context, File file) {
Drawable drawable = drawables.get(file);
if (drawable == null) {
final int targetSize = 500;
// get subsampling factor
Options opts = new Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), opts);
int largest = opts.outWidth > opts.outHeight ? opts.outWidth : opts.outHeight;
float factor = largest / (float) targetSize;
factor = Math.round(factor);
// load bitmap with subsampling
opts = new Options();
opts.inSampleSize = (int) factor;
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), opts);
drawable = new BitmapDrawable(getResources(), bitmap);
drawables.put(file, drawable);
}
return drawable;
}
I have 3 or 4 image paths that I use to load an image so I set it to an imageview. Why does it take long? Or better asking is there a way to make it faster? At the end of the day I am loading to fit an imageview of less than 60 dp hight and width
Uri mainImgeUri = Uri.parse(imagePath);
InputStream imageStream;
try {
imageStream = mActiviy.getContentResolver().openInputStream(mainImgeUri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap yourSelectedImage = BitmapFactory.decodeStream(imageStream, null, options);
mainImageIV.setImageBitmap(yourSelectedImage);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
USE CASE:
What happens is that a user will add 5 images (and he get to choose them from Gallery which is mostly taken by phone camera). He hit save and my app stores the path to them in an sqlite database. Then when the user opens the app again to see them, my app query the db to get the paths to all the images and executes the code above x number of times so all the image views are loaded with the intended images
Take a look at http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
It explains how to calculate the correct inSampleSize based on the required dimensions of the output image. It also explains how to reference large bitmaps without having to load all their pixel data into memory.
The idea is that you resample bigger images and only load the smaller ones into memory making the whole process much more efficient. The example code is accessing a bitmap from resources, but this can easily be modified for your needs.
The important things to look out for in the example are inJustDecodeBounds and calculateInSampleSize.