I got some OutOfMemory crashes when switching image in ImageSwitcher, it's very rare but it happens sometimes (never to me, otherway I would be able to debug it haha).
I only have 5 images (PNG) and each of them has something between 10-60kb, so to be honest I am quite surprised. Here is code:
frameImages = new int[]{R.drawable.f_0, R.drawable.f_1, R.drawable.f_2, R.drawable.f_3, R.drawable.f_4};
...
public void switchImage() {
int frame = getFrame();
int image = frameImages[frame];
// imageSwitcher.startAnimation(getAnimation());
if (frame == 0)
startYAxisRotation(imageSwitcher, 400);
imageSwitcher.setImageResource(image); //CRASHES HERE
}
Am I doing something wrong?
Stacktrace:
java.lang.OutOfMemoryError:
at android.graphics.BitmapFactory.nativeDecodeAsset(BitmapFactory.java:0)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:677)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:507)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:872)
at android.content.res.Resources.loadDrawable(Resources.java:3022)
at android.content.res.Resources.getDrawable(Resources.java:1586)
at android.widget.ImageView.resolveUri(ImageView.java:648)
at android.widget.ImageView.setImageResource(ImageView.java:377)
at android.widget.ImageSwitcher.setImageResource(ImageSwitcher.java:41)
at com.myapp.MainActivity$5.switchImage(MainActivity.java:143)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5293)
at java.lang.reflect.Method.invokeNative(Method.java:0)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
at dalvik.system.NativeStart.main(NativeStart.java:0)
With the below code, you can be able to reduce the resolution of the Bitmap prior to loading it in the ImageSwitcher.
public void switchImages(){
//Create a Bitmap with the resolution based on the view size.
//getWidth and getHeight will not work until the layout as finished,
//if you need to show an image when the Activity is created you need
//to follow this link: https://stackoverflow.com/a/6569243/2910520
Bitmap smallBitmap = decodeSampledBitmapFromResource(getResources(), yourDrawableId,
imageSwitcher.getWidth(), imageSwitcher.getHeight())
//use a drawable, because you can create it from a custom Bitmap
BitmapDrawable drawable = new BitmapDrawable(getResources(), smallBitmap);
imageSwitcher.setImageDrawable(drawable);
}
These two methods below were copied from developer site here
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
unfortunately if None of the Above works, then Add this to your Manifest file. Inside application tag
<application
android:largeHeap="true"
for details
Strange out of memory issue while loading an image to a Bitmap object
Add android:largeHeap="true"
in your Manifest file.
hope solve your issue.
So, solution was to decrease resolution of images (I did manual resizing in photoshop). From 620x1080 to 800x460 and everything works fine.
Conclusion: Image size it's not that important - image resolution is.
PS: MatPag should also be teoretically correct, it's practically same what I did - except he is doing that in code, I did it manually in photoshop by myself. But I think doing that manually is better IMHO.
Related
I'm trying to setImageBitmap but I keep getting the following OutOfMemoryError even after I have implemented the guidelines here https://developer.android.com/topic/performance/graphics/load-bitmap.html:
java.lang.OutOfMemoryError: Failed to allocate a 11345668 byte allocation with 1206384 free bytes and 1178KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:620)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:455)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:478)
at com.example.myapp.ImageOptimized.decodeSampledBitmapFromResource(ImagenOptimizada.java:50)
at com.example.myapp.Game3Players.onAnimationEnd(Game3Players.java:511)
at android.view.animation.Animation$3.run(Animation.java:381)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
This is the part of the code where I use the method. I randomly choose one card to show one image, and the rest will show another:
ImageView[] cardsArray = new ImageView[3];
cardsArray[0]=cardOne;
cardsArray[1]=cardTwo;
cardsArray[2]=cardThree;
final int index = new Random().nextInt(cardsArray.length);
cardsArray[index].setImageBitmap(ImageOptimized.decodeSampledBitmapFromResource(getResources(), R.drawable.skull, 250, 250));
for (int i=0; i<cardsArray.length; i++){
if (cardsArray[index]!=cardsArray[i]){
cardsArray[i].setImageBitmap(ImageOptimized.decodeSampledBitmapFromResource(getResources(), R.drawable.safe, 250, 250));
}
}
I first tried this because, since I'm replacing an image for another, I want to use the same width and height:
cardsArray[index].setImageBitmap(ImageOptimized.decodeSampledBitmapFromResource(getResources(), R.drawable.skull, cardsArray[index].getWidth(), cardsArray[index].getHeight()));
But this was giving me an error right away. That's why I decided to use a fixed size, 250, but it keeps throwing me the error. What's funny is that sometimes the code runs without a problem, but then when the operation is repeated - maybe the third, fourth, sometimes fifth time - it crashes. Am I possibly doing something wrong which leads to a memory leak maybe?
Just in case you also wanna take a look to the class where I follow the guidelines:
public class ImageOptimized {
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
}
You can use loading and caching image libraries for better performance such as Glide or Picasso.
Usage of Glide, for example, is pretty simple:
Glide.with(this).load(R.drawable.safe).into(imageView);
You can resize them too:
Glide.with(this).load(R.drawable.safe).apply(new RequestOptions().override(250, 250).into(imageView);
Most of these libraries handle by their own the cleaning of resources they used; which means that they handle memory efficiently.
Also try not to use very large resolution images and use PNG format if you can choose.
I had the same problem days ago, I missed the copy/paste of my drawable and instead of putting it on drawable-hdpi (for instance) I put it on drawable folder, without extension, moving the drawable to a drawable folder with extension worked for me.
Also you can add these lines into your manifest.xml
android:hardwareAccelerated="false"
android:largeHeap="true"
I have problem with shadow of ActionBar it doesn't show when i add this : android:hardwareAccelerated="false" in my AndroidManifest.xml but when i delete this line it show shadow well and show other problem in my Activity which has Recycleview with items of images(small size images) it be very slow when i scroll the Recycleview, but when add android:hardwareAccelerated="false" scrolling be normal.
Please anyone could help me with this?
According to google documentation
Beginning in Android 3.0 (API level 11), the Android 2D rendering pipeline supports hardware acceleration, meaning that all drawing operations that are performed on a View's canvas use the GPU. Because of the increased resources required to enable hardware acceleration, your app will consume more RAM.
So i guess writing android:hardwareAccelerated="false" stops the app from making use of the GPU, hence reducing GPU rendered effects like shadows.
A couple of things that you can do is
Add the android:hardwareAccelerated="false" for the specific activity where you want to load heavy images. Click here to check that.
Try and reduce the size of the images that you are loading. This can be done using the following two functions as documented in the developer documentation for android. Click here for the complete documentation.
Function 1
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Function 2
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Set Image in ImageView
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Also refer this link for a another reference about Image Resizing.
I have bigs problems with the decodeSampleBitmap code and the final size of my images. I have my drawables in nodpi drawables folder for not multiply the same drawable in diferents folders with diferents sizes, and i resize after aply my decodeSampleBitmap.
I have this code for load a bitmap:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
The code i can get at the documentation. Ok i think this is for load images more eficiently but i have memory problems. I try explain this problem.
1-I want load a png, 2037x1440 pixels, 12.6kb.
2-I aply my code and load a png with 2037x1440 pixels and 11,73 MB. I dont undurstand what i'm doing wrong. Why android multiply for 1000 the size of my drawables?
10-12 12:06:07.842 12405-12822/com.viridesoft.kiseichuu V/control4: width=2037 height=1440 size=11733120
Edit: if i load the drawable with BitmapFactory.decodeResource the size is the same, 11733120.
The old question should be changed. I understand i cant reduce more my image size and i continue with the same problem.
I load a lot of images for create a dinamical background and charge all the characters(my player and zombies), in total until 111 bitmaps of 25%px x 10%px of the screen size and until 30 bitmaps of the 100% screen size. I load all this bitmaps in a loader activity and save for the later use in the game. I have a good quality of images and a drawable-nodpi folder with a lot of images for the best android resolution i see. My game run fluid in some devices (bq aquaris, nexus 5, etc) but is continually cleaning the memory and run slow in others (some tablets, some mid-range devices, etc )
The new question is:
How i can load eficiently a lot of images for a fluid game experiencie?
I have a set of image files stored in internal storage, each file size is about 750 KB. I need to create a 360 degrees animation like with this images, so, I load each image in a list, while I'm doing this process an out of memory exception appears.
I have been reading about bitmap processing on Android, but in this case is not about resize bitmap dimensions, the dimensions are OK (600, 450) because its a tablet application, is about image quality I think.
Is there a way to reduce the memory each bitmap takes?.
It is not possible without reducing image dimensions.
All images with the same dimensions require same amount of RAM, regardless it size on disk and compression. Graphics adapter don't understand different image types and compression and it needs only uncompressed raw array of pixels. And it size is constant
For example
size = width * height * 4; // for RGBA_8888
size = width * height * 2; // for RGB_565
So you should reduce image dimensions or use caching on the disk and remove bitmaps from the RAM that are currently invisible and reload from disk when needed.
There is a great resource on how to do this here:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Basically you need to load a bitmap at a different resolution using these two functions:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Then set the image as follows:
imageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),
R.drawable.my_image_resource,
imageView.getWidth(),
imageView.getHeight()));
Hope that helps!
I am implementing an application in which an activity shows a image view with full screen. Now i have some images downloaded in application's file directory. I want to set image to image view in that activity for which i am scaling down the larger images. I am getting OutOfMemory error after 10 to 15 mins of using application. error is in decodeFile of BitmapFactory. Here is my code :
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromFile(String imagePath,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(imagePath, options);
}
Right now i am giving reqHeight = 500 and reqWidth = 500
This code is given in Android guidelines page. Is there anything i am missing, if so please help me out. I tried lots of things but not getting any solution.
Change you int inSampleSize = 1; to int inSampleSize = 2 or 3 and test the application again, I hope this will solve your problem. I also suggest insted of this technique,try lazy loading for all you image dowanload by doing so there will be no bitmap and outOfMemoryError. Yo can have lazy loading - https://github.com/thest1/LazyList
Try using this
Bitmap resizedbitmap2 = Bitmap.createScaledBitmap(your_bitmap, width, height,
true);
it will help you.
There might be a memory leak in your app if you are storing list of bitmaps returned by decodeSampledBitmapFromFile somewhere. Try to check for memory leak and also use Bitmap.recycle() to recycle bitmaps and help android free memory.