hi i am new to android and i have come across some memory management issues whilst using xml to position and design my activity layouts.
most images are around 100kb but vary in size e.g. image 1 will be 512x512, image 2 will be 120x320 etc.
at the moment the images are slowing down my app's performance and sometimes crashing.
Is there a way to reduce the amount of memory an image takes up on an app?
There's a number of steps that applications must go through in order to handle bitmaps sanely.
Small Compressed Size. It's important to balance quality vs. file size for your on-disk (or on-wire) formats. Being able to run PNG files through a lossy pre-processor, or choosing to use WEBP/JPG where needed are critical for each image in your app. Smaller PNG Files covers this more. The problem here, however, is that this doesn't help you with in memory size. Remember when your images are loaded from disk, they are decompressed into 32 bits-per-pixel in memory (in other words, no compression).
Compressed In Memory Format. Android provides the alternate 565 format, which uses only 16 bits per pixel, instead of the 32 bits for the 8888 format. If you're using an image that doesn't need alpha, you should consider the process discussed in Smaller Pixel Formats to leverage loading a bitmap as a 565.
Re-Using bitmap space. Most applicaitons that use thumbnails, only really have 10-20 of them visible on screen at one time (even though there may be thousands to load). The trick here is described in Re-using bitmaps. Basically, once a thumbnail is no longer needed, you can re-use it's space for an incoming thumbnail, rather than allocating a brand new one.
Display resolution. It makes no sense to load a 2MB image, to only display it as a thumbnail. Instead, you should be scaling the image to the resolution of what it'll display at, on the device. I discuss the most efficient way to load these images in the other SO post.
In general, Libraries like Picasso and Glide do a good job at providing APIs that make all this easier; but they are still going through these same processes under the hood.
You have 3 solutions you can do:
1st Solution:
Add in your AppManifest.xml in your application tag:
android:largeHeap="true"
This will try to prevent your app from causing OutOfMemoryError, but use it with caution.
Documentation: Whether your application's processes should be created with a large Dalvik heap. This applies to all processes created for the application. It only applies to the first application loaded into a process; if you're using a shared user ID to allow multiple applications to use a process, they all must use this option consistently or they will have unpredictable results.
Most apps should not need this and should instead focus on reducing their overall memory usage for improved performance. Enabling this also does not guarantee a fixed increase in available memory, because some devices are constrained by their total available memory.
2nd Solution:
If your images' file size are large, you can minimize them by using this online tool: http://compresspng.com/
3rd Solution:
You can use BitmapFactory for loading your images. Here is the Android Developers documentation: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Related
Apologies in advance for such a basic question, but this is my first app and I can't quite find a clear answer for my situation. All the images in my app are stored in the drawable folder, I'm NOT downloading any images from the internet. All the information I come across when it comes to multiple image sizes seems to refer to the occasion when the app is fetching images from the internet.
So currently most the images in my app are one size, customized for the largest size - xxxhdpi. However, I understand the app is doing some work to "shrink down" those images for the xxhdpi size screens.
I'm having second thoughts about this one size fits all approach. I'm thinking that perhaps the app doing the work to shrink the image down might take up extra memory and negatively impact performance. I've been looking at the Android Studio Profiler and I've been trying to understand the Graphics Process when I look at the Memory Graph.
More generally speaking, is there a benefit to having the smallest size images possible, even for the xxxhdpi? For example, does it hurt (memory wise or in some other aspect) to use a .png image when I could use a lower quality jpg? Again, just to super clear, this is just in the scenario when the app has all of its images in the drawable folder. My app has options where players can change the game background and other images so I want to be sure I'm optimizing how the images for best performance. Thanks.
Memory. If you load a bitmap of x by y pixels, in memory that takes 4*x*y bytes. For a full screen image, you can expext that to be 4000*1000*4 or 16 MB. That's a good chunk of memory to a small device, which also tends to have less RAM. If instead it needed one at half the resolution, you would have 2000*500*4, or 4 MB.
Obviously this scales with size. The smaller your images, the less memory wasted. I wouldn't argue that you need to provide every size, but if you're using large images I'd provide more than one. Also, for anything that isn't incredibly complex (like icons) I'd consider vector images instead (although that's a CPU time vs memory tradeoff).
You mentioned png vs jpg. There's two things to consider there: apk size and image quality. JPG is smaller, so it will lead to a smaller apk size. PNG is lossless, so it will have higher quality (although whether that matters requires a human visual check- it matters less than you'd think on a lot of things). Interestingly it doesn't effect the amount of memory used at runtime, because both are held in the Bitmap object uncompressed.
I'm building an application with very big sized images.
Almost all of my UI components are made of ImageViews.
I only have to show 12 images(ui components) on my first activity, but it consumes 80mb on startup.
The images are divided into each drawable directories using Android Drawable Importer.
By doing this I was able to reduce the runtime memory(which I can see on the Android studio's device monitor) to half, but it is still consuming 80~120mb of memories, which I believe is too much.
The first question is, isn't 80~120mb too much for a four screen(two activities, three fragments) application?
The second is, if it's too much then, what and how can I do to reduce memory usage?
When working with images keep in mind that there is a HUGE difference between compressed format (jpg, png..) and Bitmap. Computing the size of a Bitmap is pretty easy, it's width * height * 4 bytes (assuming that the bitmap has the default configuration argb888). So a full hd image that compressed is xy kb, when decompressed will occupy 8294400 bytes (~8mb). So my advice to reduce memory consumption is... scale down your images. You're asking if 80-120 mb is too much, well it seems like a lot but it really depends on what you're doing. What happen if you force garbage collection (there should be an icon in the device monitor)?Another thing to take into account is how to decompress the images, refer to this and use a library (Picasso, Glide..).
Is it alright to use at all Background images?
I once had a bad experience using background images because they use a lot of memory when loaded, even causing OOM exceptions in some devices.
I have almost thought that it is a bad practice, as most of the apps have simple backgrounds/effects in Android, compared to their iOS counterparts.
Hence, I only use them on login pages or other pages that are only shown occasionally.
Moreover, whatsapp still uses its fullscreen background, so it makes me think it is maneagable in some way, or probably WA doesn't need much memory.
Or is it enough to simply pass the image to the nodpi drawable folder?
I've used images as background on several apps ( some of them commercial )
Generally you can use .png for simple graphics without too many colors.
If you use a .jpg file (photos) you can reduce the quality of the saved image. I usually use "gimp" to do this. You can reduce the quality very substantially without impacting what the user sees. The result is a very small file.
I look forward to better SVG support going down the road...but we are not there yet.
When you are loading them you can use Config.RGB_565 to help reduce your memory footprint.
Why do we have to provide images for all screen sizes when developing mobile applications? Wouldn't it be more efficient to just have 1 very large image for each unique image and then scale the image down whenever the app is being run on a smaller device? It would definitely make the game's file size much smaller.
In lots of cases, it wouldn't look as good.
If you find a set of well designed icons, you'll see that they've been independently designed for each resolution: the smaller ones will deliberately have less detail in, because downscaling just doesn't produce as good results.
Here are two GNOME icons, for the same thing, but one at 256x256 and one at 48x48. You can see that the 48x48 one has less detail in the writing on the letter, but the writing is also designed rather differently: on the 256x256 one it looks like the middle page of a document, and on the 48x48 one it looks like the opening of a letter, with an address at the top.
It would make the size of the .apk file significantly smaller, but it would have more undesirable tradeoffs for runtime efficiency.
Having to load large bitmap objects and scale them down is an un-necessary load for the device's processor. But more importantly, having to load large bitmap objects into memory makes the VM's memory fill up more quickly. This means that the VM has to do garbage collection more often, which can cause noticeable delays at runtime (this can cause animations to lose frames and look rougher).
From experiments and from reading other posts like this one it seems that it's hard to process high resolution images on Android because there is a limit on how much memory the VM will allow to allocate.
Loading a 8MP camera pictures takes around 20 MB of memory.
I understand that the easy solution is to downsample the image when loading it (BitmapFactory offers such an option) but I still would like to process the image in full resolution: the camera shoots 8MP, why would I only use 4MP and reduce the quality.
Does anyone know good workarounds for that?
In a resource-constrained environment I think that your only solution is to divide and conquer: e.g. caching/tiling (as in: tiles)
Instead of loading and processing the image all at once you load/save manageable chunks of the image from a raw data file to do your processing. This is not trivial and could get really complex depending on the type of processing you want to do, but it's the only way if you don't want to comprise on image quality.
Indeed, this is hard. But in case image is in some continuous raster format, you can mmap it
( see java.nio.ByteBuffer ) - this way you get byte buffer without allocating it.
2 things:
Checkout the gallery in Honeycomb. It does this tiled based rendering. You can zoom in on an image and you see then that the current part is higher res then the other parts. If you pan around you see it rendering.
When using native code (NDK) there is not a resource limit. So you could try to load all the data native and somehow get parts of it using JNI, but I doubt it's better then the gallery of honeycom.