Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I'm new to Android so I'm not getting exactly how to make my application more responsive as it creates bitmaps for each processing and set to imageView.Basically What i'm trying to do is that create a bitmap, play with it,like passing values from seekBar to change its properties and set it to imageView.How to create a Copy of Bitmap object to avoid references.Any Suggestions ?? Thanks in advance
You can try this library which handles bitmap very efficiently.
https://github.com/thest1/LazyList
Its very easy to use this lazy list library.
It does the job of caching bitmap automatically:-
ImageLoader imageLoader=new ImageLoader(context);
imageLoader.DisplayImage(url, imageView);
NOTE :
Don't forget to add the following permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Please create only one instance of ImageLoader and reuse it all around your application. This way image caching will be much more efficient.
and also you can look into Nostras ImageLoader, as it efficiently handles loading images into specific sized containers, i.e resizing and compressing them, before you will need to handle them. It also supports content uris which will help you all at once.
Besides this,it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x96 pixel thumbnail in an ImageView.
You should load a scaled down version of image into memory.
I am also sharing wonderful utility class for bitmap which helps you to scale down your image according to size:-
BitmapUtil.java
/**
* Provides static functions to decode bitmaps at the optimal size
*/
public class BitmapUtil {
private BitmapUtil() {}
/**
* Returns Width or Height of the picture, depending on which size is smaller. Doesn't actually
* decode the picture, so it is pretty efficient to run.
*/
public static int getSmallerExtentFromBytes(byte[] bytes) {
final BitmapFactory.Options options = new BitmapFactory.Options();
// don't actually decode the picture, just return its bounds
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
// test what the best sample size is
return Math.min(options.outWidth, options.outHeight);
}
/**
* Finds the optimal sampleSize for loading the picture
* #param originalSmallerExtent Width or height of the picture, whichever is smaller
* #param targetExtent Width or height of the target view, whichever is bigger.
*
* If either one of the parameters is 0 or smaller, no sampling is applied
*/
public static int findOptimalSampleSize(int originalSmallerExtent, int targetExtent) {
// If we don't know sizes, we can't do sampling.
if (targetExtent < 1) return 1;
if (originalSmallerExtent < 1) return 1;
// test what the best sample size is
int extent = originalSmallerExtent;
int sampleSize = 1;
while ((extent >> 1) >= targetExtent) {
sampleSize <<= 1;
extent >>= 1;
}
return sampleSize;
}
/**
* Decodes the bitmap with the given sample size
*/
public static Bitmap decodeBitmapFromBytes(byte[] bytes, int sampleSize) {
final BitmapFactory.Options options;
if (sampleSize <= 1) {
options = null;
} else {
options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
}
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
}
}
also You can go to this link It will help you to build the app
http://developer.android.com/training/displaying-bitmaps/index.html
Use Lazy loading and Image loader classes in android to set image on imageView so it will look like more responsive
Here is some tutorial links for this
Tutorial 1
Tutorial 2
Related
I have a viewPager that have 4 images that need to resize. I implemented different options but no options run. I write the different options:
In getView put:
image.setImageBitmap(bitmap);
The bitmap obtain:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(bitmapOriginal, null, options);
int sampleSize = 1;
while ((options.outHeight > 600 * sampleSize) &&(options.outWidth > 400 * sampleSize)) {
sampleSize *= 2;
}
options.inJustDecodeBounds = false;
options.inMutable = false;
options.inSampleSize = sampleSize;
Bitmap bm=BitmapFactory.decodeStream(bimapOriginal, null, options);
But it's very slowly.
I put the last code in AsyncTask() but I can't scroll quickly.
I override the
onPageScrollStateChange(int state)
I only show the image when the state is SCROLL_STATE_IDLE but the effect is the same as in the other cases.
I use LruCache, I save the images in LruCache in Asynctask.
In instantiateItem I put
for(int i=position *4;i<(position +1)*4 +4;i++) {
//NameImage is an Array with the name of image. Every time that calling instantiateItem save 4 images in LruCache
new BitmapWorkerTask(nameImage[i).executorOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
But the problem is the same as last cases, I can't scroll quickly and the app are so slowly.
I have images with a big quality and size. How can I do to scroll quickly in viewPager?
The problem is that even though your work is being done in AsyncTask but the ViewPager is waiting for you to return the image(in getView(), instanciateItem(),newView() etc whatever you are using). What you need to do is create several instances of ImageViews and return one of them almost immediately(this speeds things up). Let the AsyncTask do its loading and when it returns(to the corrct instance identified by position), the image is set to the instance which is already in the ViewPager by now.
Create an Array of imageviews to use as buffer and identify them using the position attribute of list item.
ImageView[] bufferView=new ImageView[4]; //global
//use Async task something like this
#Override
protected void onPostExecute(Bitmap result) {
virtualPosition = position % 4; //Real position % (number of ImageViews)
bufferView[virtualPosition].setImageBitmap(result);
}
return this bufferView[virtualPosition] as ImageView for this item
EDIT: Don't forget to add it to parent ViewGroup too
container.addView(pageview[position]);
I wrote an Android Image Manager that handles caching transparently (memory and disk). The code is on Github.
It uses a Handler instead of AsyncTasks. I've been using it for an image heavy application that contains grids and lists all around, the scrolling and load performance has improved significantly. Check it out: https://github.com/felipecsl/Android-ImageManager
I've got a serious performance issue in my app, when loading up bitmaps it seems to take up way to much memory.
I have a drawable folder which contains the bitmap sizes for all android devices, these bitmaps are of high quality. Basically it goes though each bitmap and makes a new one for the device depending on the size. (Decided to do it this way because it supports the correct orientation and any device). It works but it's taking up way to much memory and takes along time to load. Can anyone make any suggestions on the following code.
public Bitmap getBitmapSized(String name, int percentage, int screen_dimention, int frames, int rows, Object params)
{
if(name != "null")
{
_tempInt = _context.getResources().getIdentifier(name, "drawable", _context.getPackageName());
_tempBitmap = (BitmapFactory.decodeResource(_context.getResources(), _tempInt, _BM_options_temp));
}
else
{
_tempBitmap = (Bitmap) params;
}
_bmWidth = _tempBitmap.getWidth() / frames;
_bmHeight = _tempBitmap.getHeight() / rows;
_newWidth = (screen_dimention / 100.0f) * percentage;
_newHeight = (_newWidth / _bmWidth) * _bmHeight;
//Round up to closet factor of total frames (Stops juddering within animation)
_newWidth = _newWidth * frames;
//Output the created item
/*
Log.w(name, "Item");
Log.w(Integer.toString((int)_newWidth), "new width");
Log.w(Integer.toString((int)_newHeight), "new height");
*/
//Create new item and recycle bitmap
Bitmap newBitmap = Bitmap.createScaledBitmap(_tempBitmap, (int)_newWidth, (int)_newHeight, false);
_tempBitmap.recycle();
return newBitmap;
}
There's an excellent guide over on the Android Training site:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
It's about efficient loading of bitmap images - highly recommended!
This will save space. If not using Alpha colors it would be better not to use one with the A channel.
Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
// or Bitmap.Config.RGB_565 ;
// or Bitmap.Config.ARGB_4444 ;
Bitmap newBitmap = Bitmap.createScaledBitmap(_tempBitmap, (int)_newWidth, (int)_newHeight, options);
I know this must be one of the most asked things at SO, but none of the other answers gave me a solution. But from reading the other answers, looks like I'll need to redesign the way the App is working.
It's like this, we have a ScrollView, which will inflate some views. A ListView can't be used in this situation, because to behave the way we want it would require extending the ListView, and this is something we don't want to do (even though this seems to be our only solution to our current way of showing items, because of this OOM exception). The list can have a lot of columns per row, and the bigger the screen, more columns it will have.
Each inflated View has a layout displaying some info from the database, including a picture. This picture is stored through a byte array. It's any picture taken with the device camera. Currently every photo (byte array) is taking 800kb to 1mb, which seems a lot to me. Now the list have 30+ items. I took photos until the OOM happened, and it happened when I took a total of 6 photos (occasionally 7). That would be 8mb-9mb of data. Everytime I go to other Activity, and go back to the Activity the ScrollView is in, the list needs to be repopulated.
This is the snippet of the PopulateList method:
if (item.getImg() != null) {
if (App.debug) {
Log.d(TAG, "Setting bmp.");
}
Bitmap bmp = App.byteArrayToBmp(item.getImg());
imgV.setImageBitmap(bmp);
}
Every inflated View will open an 'Advanced Dialog', which will contain other info. Maybe the Image could be there instead on the list (meaning that there would be only 1 bitmap, as every inflated View shares the same advanced dialog). Or I could extend the ListView and benefit from it recycling method (It's not a good solution as I though it would be considering more than 6 items can be at the screen). Another thing that bothers me is every picture having 800kb. Seems like a lot for a 128x128.
This is the setup for the size:
cameraParams.setPictureSize(App.pxToDpi(128), App.pxToDpi(128));
cameraParams.setPictureFormat(PixelFormat.JPEG);
camera.setParameters(cameraParams);
public static int pxToDpi(int px) {
final int scale = app.getApplicationContext().getResources().getDisplayMetrics().densityDpi;
int pixels = (int) px * (scale / 160);
return pixels;
}
So, do you think there is a solution to my issue keeping the current model of my App, or will I need to reformulate?
EDIT: The bitmap method:
public static Bitmap byteArrayToBmp(byte[] byteArray) {
Bitmap img = null;
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2;
img = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, opts);
return img;
}
You might want to look at the Official Android Training docs, they've just been updated:
Check out Displaying Bitmaps Efficiently with the lesson: Loading Large Bitmaps Efficiently that goes over this.
Basically you can decode the image using sampleSize to decode it to the width and height you want:
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;
}
Explained in much great detail in the links above
Memory Issues
So I am writing an app that should be able to page through detail views that have one large 640 x 480 image on top and 3 images that are part of a gallery that is being lazy loaded. Following Google design guidelines this is what they suggest doing. I can page through maybe 12 - 13 fragments before it crashes because of being out of memory. I think that there are a couple of culprits in this problem.
1.) I am using the FragmentStatePager. Shouldn't this be destroying the fragments that are not being viewed when memory becomes an issue? This is not happening. I thought it was automatic. What do I have to do to make this happen? Could it have something to do with how I have my Fragment implemented? I do all of my Activity config in onCreateView. For the sake of thoroughness I've included the source for this. Plain Vanilla here:
public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
return InventoryDetailFragment.newInstance(position);
}
}
2.) I have a method that is trying to figure out the size of the image that needs to be downloaded without placing it in memory. Then compresses the image while downloading it to the required size. This is not successfully implemented. But I'm not sure what is going wrong.
private Bitmap downloadBitmap(String url, int width, int height) {
Bitmap bitmap = null;
int scale = 1;
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
bitmap = BitmapFactory.decodeStream((InputStream)new URL (url).getContent(), null, options);
if (options.outHeight > height || options.outWidth > width) {
scale = (int) Math.max(((options.outHeight)/ height), ((options.outWidth)/ width)); }
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
bitmap = BitmapFactory.decodeStream((InputStream)new URL (url).getContent(), null, o2);
cache.put(url, new SoftReference<Bitmap>(bitmap));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Error e){
Log.d("TEST", "Garbage Collector called!");
System.gc();
}
return bitmap;
}
I have tried everything that I know how to do but it's beyond my meager grasp of Android/Java. Please help! Thanks!
There are a few things that you need to change:
This is a horrible idea: BitmapFactory.decodeStream((InputStream)new URL (url).getContent(), null, options); You're getting the image from the web each time this is executed (so twice in the code you posted). Instead, you need to download the image and cache it locally.
Add logic to your fragments to call recycle() on the bitmaps as soon as the fragment is detached. Add logic to always reload the image (from the cache) whenever the fragment is attached.
Lastly, your inSampleSize calculation is wrong. inSampleSize should be a value that's a power of two, e.g. 1,2,4,8. You can use logarithms or simple binary logic to get the right one, this is what I use, which will always downsample using at least 2 (only call this if you know that the image is too big):
-
int ratio = (int) Math.max((height/options.outHeight), ( width/options.outWidth); //notice that they're flipped
for (int powerOfTwo = 64; powerOfTwo >=2; powerOfTwo = powerOfTwo >> 1 ) { //find the biggest power of two that represents the ratio
if ((ratio & powerOfTwo) > 0) {
return powerOfTwo;
}
}
if you realize your graphics with opengl, this would not counted to memory.
Another ooption is to use
android:largeHeap="true"
in the manifest. Could be working.
did you use ddvm to search for memory leaks?
http://www.youtube.com/watch?v=_CruQY55HOk
I'm having an odd problem with my map pin sizes. To preserve dynamic-ness, the map pins for different categories are stored on a site's server so that they can be changed at any point even after the app is published.
I'm caching the pins every time I download them and I only ever re-download them if the server sends back a bit saying that one has changed since last I downloaded it. The first time I grab the pins, I use the bitmaps before I save them to files and the map markers are the correct size. Every time after that I'm loading a saved version of the pins straight from the image file. These are displaying considerably smaller than they are when using the bitmaps from the first download.
At first, I thought it was a problem with the way I'm saving the PNGs, but their sizes are correct (64 x 64). Is this a dip/px issue or do I need to decompress the image files with some sort of option?
Here's how I grab the images the first time:
public static Bitmap loadMapPin(String category, int width, int height) {
URL imageUrl;
category = category.toLowerCase().replace(" ", "");
try {
imageUrl = new URL(PIN_URL+category+".png");
InputStream is = (InputStream) imageUrl.getContent();
Options options = new Options();
options.inJustDecodeBounds = true; //Only find the dimensions
//Decode without downloading to find dimensions
BitmapFactory.decodeStream(is, null, options);
boolean scaleByHeight = Math.abs(options.outHeight - height) >= Math.abs(options.outWidth - width);
if(options.outHeight * options.outWidth >= width * height){
// Load, scaling to smallest power of 2 that'll get it <= desired dimensions
double sampleSize = scaleByHeight
? options.outHeight / height
: options.outWidth / width;
options.inSampleSize =
(int)Math.pow(2d, Math.floor(
Math.log(sampleSize)/Math.log(2d)));
}
options.inJustDecodeBounds = false; //Download image this time
is.close();
is = (InputStream) imageUrl.getContent();
Bitmap img = BitmapFactory.decodeStream(is, null, options);
return img;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
And here's how I'm loading them from the cached file:
BitmapFactory.decodeFile(filepath);
Thanks in advance!
I've found that, by default, decompressing an image to a bitmap doesn't scale with high density screens. You have to set the density to none. In other words, you specify that the image is meant for an unknown density.
Solution:
Bitmap b = BitmapFactory.decodeFile(filepath);
b.setDensity(Bitmap.DENSITY_NONE);