OutOfMemoryError: bitmap size exceeds VM budget - android

Sorry it seems like a repeated question, BUT I think I don't qualify to any of the recommendations already posted.
I've a Gallery of maximum 20 images on my application. After playing a while flinging back and forth I'm getting OutOfMemoryError.
The strange thing is that I don't hold any static references, and I've searched for possible memory leaks I can assure that I've not found one so far.
Anyway, 20 images (PNG of 100KB on average) doesn't be like that much. And I've implemented a view cache, SoftReference holders for the bitmaps, etc.
Is it 20 PNG images of 100KB on average enough to kill my app?? seriously? how can I get rid of this? I've followed this great post also
http://blog.jteam.nl/2009/09/17/exploring-the-world-of-android-part-2/
Any more ideas?
This is the ImageCache:
public class AsyncImageLoader {
private final String TAG = getClass().getSimpleName();
private Context mContext;
private HashMap<String, SoftReference<Bitmap>> mImageCache;
public AsyncImageLoader(Context context) {
mContext = context;
mImageCache = new HashMap<String, SoftReference<Bitmap>>();
}
public Bitmap loadImage(final String identifier, final String imagePath, final ImageCallback imageCallback) {
if (mImageCache.containsKey(imagePath)) {
SoftReference<Bitmap> softReference = mImageCache.get(imagePath);
Bitmap bitmap = softReference.get();
if (bitmap != null) {
Log.i(TAG, "Retrieving image from cache: " + imagePath);
return bitmap;
}
}
final Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
imageCallback.imageLoaded((Bitmap) message.obj, imagePath, identifier);
}
};
new Thread() {
#Override
public void run() {
Bitmap bitmap = loadImageFromPath(imagePath);
mImageCache.put(imagePath, new SoftReference<Bitmap>(bitmap));
Message message = handler.obtainMessage(0, bitmap);
handler.sendMessage(message);
}
}.start();
return null;
}
public Bitmap loadImageFromPath(String path) {
if(!GeneralUtilities.isEmpty(path)) {
Log.i(TAG, "Loading image: " + path);
InputStream imageInputStream = null;
try {
final AssetManager assetManager = mContext.getResources().getAssets();
imageInputStream = assetManager.open(path);
Bitmap bitmap = GeneralUtilities.decodeFile(imageInputStream);
imageInputStream.close();
return bitmap;
} catch (final IOException e) {
Log.e(TAG, e.getMessage());
}
}
return null;
}
public interface ImageCallback {
public void imageLoaded(Bitmap imageBitmap, String imagePath, String identifier);
}
}
and the method GeneralUtilities.decodeFile is:
public static Bitmap decodeFile(InputStream is){
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, o);
//The new size we want to scale to
final int REQUIRED_SIZE=140;
//Find the correct scale value. It should be the power of 2.
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while(true) {
if(width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(is, null, o2);
}
And in the getView of the ArrayAdapter I've something like this:
final ImageView itemImage = cache.getHistoryImage();
//final ImageView itemFrame = cache.getFrame();
String filename = item.getFilename().trim();
itemImage.setTag("front_" + filename);
Bitmap cachedImage = mAsyncImageLoader.loadImage("front_" + filename, filename, new ImageCallback() {
public void imageLoaded(Bitmap imageBitmap, String imagePath, String identifier) {
ImageView imageViewByTag = (ImageView) mGallery.findViewWithTag(identifier);
if (imageViewByTag != null) {
imageViewByTag.setImageBitmap(imageBitmap);
}
}
});
itemImage.setImageBitmap(cachedImage);

There seems to be a bug in the Android framework, although Google seems to deny it.
Did you read through issue 8488?
http://code.google.com/p/android/issues/detail?id=8488
I am not sure if this applies to your code - but you might try the recommendations before setting/updating the image on the ImageView.
Basically, it boils down to calling Bitmap.recycle(), nulling references (probably irrellevant in your case) and explicitly calling calling System.gc().
The garbage collector seems to run asynchronously and a new might fail even though memory could be freed.

Related

Downloading images from server and applying them as background for dynamically created buttons

Scenario : I have to download the images from server and set them as background for dynamically created buttons. Number of images keep changing, so I have to create that many number of buttons dynamically.
What I have completed so far : I successfully managed to download the images using AsyncTask and set them as background for dynamically created buttons. If there is no internet connection or if images are not downloaded for some reason then setting the background of each buttons to some default image.
Problem : Everything works so fine on phone, i.e downloading images and creating buttons dynamically depending upon the number of images and setting images as background for them. Now when I run the same program on Tablet it is not working. Buttons are not getting generated dynamically. But if I turn of the internet connection and then run, wonder happens.! Everything works fine i.e buttons are dynamically generated with the default image as their background. I'm not able to understand why is it so.! Is there any problem with AsyncTask? Any help will be appreciated..
Here is the AsyncTask
public class GetsImages extends AsyncTask<String, Void, Bitmap> {
FileCache fileCache;
ProgressDialog pd;
String url;
GetImages task = null;
Context context;
private Bitmap image;
HomeDynamic activity;
AsyncImage responseImage = null;
public GetImages(String url, HomeDynamic activity) {
this.url = url;
// this.context = context;
this.responseImage = activity;
attach(activity);
fileCache = new FileCache(activity);
//pd = new ProgressDialog(activity);
}
public GetImages(Context context) {
this.context = context;
}
protected void onPreExecute() {
super.onPreExecute();
/*pd.setTitle("Processing");
pd.setMessage("Please wait...");
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();*/
}
#Override
protected void onPostExecute(Bitmap result) {
/*if (pd.isShowing())
pd.dismiss();*/
// studentPic.setImageBitmap(result);
responseImage.processImage(result);
}
public void detach() {
activity = null;
}
public void attach(HomeDynamic activity) {
this.activity = activity;
}
public static void CopyStream(InputStream is, OutputStream os) {
final int buffer_size = 1024;
try {
byte[] bytes = new byte[buffer_size];
for (;;) {
int count = is.read(bytes, 0, buffer_size);
if (count == -1)
break;
os.write(bytes, 0, count);
}
} catch (Exception ex) {
}
}
#Override
protected Bitmap doInBackground(String... arg0) {
// TODO Auto-generated method stub
File f = fileCache.getFile(url);
// from SD cache
Bitmap b = decodeFile(f);
if (b != null)
return b;
// from web
try {
Bitmap bitmap = null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) imageUrl
.openConnection();
conn.setConnectTimeout(15000);
conn.setReadTimeout(15000);
conn.setInstanceFollowRedirects(true);
InputStream is = conn.getInputStream();
OutputStream os = new FileOutputStream(f);
CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
private Bitmap decodeFile(File f) {
try {
// decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = 64;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 < REQUIRED_SIZE
|| height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new
FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {
}
return null;
}
}
Without showing any code that you did, it is very difficult to figure out where the problem is. If your activity involves so many photos, I suggest using Picasso library which will do excellent job for handling images.

Bitmap Memory leak - Android

I'm having a memory leak in my app which is triggering GC a number of times and causing performance issues. I generated a leak suspect report using MAT. Here is the report:
Problem Suspect 1:
One instance of "android.graphics.Bitmap" loaded by "" occupies 4,194,368 (20.13%) bytes. The memory is accumulated in one instance of "byte[]" loaded by "".
Problem Suspect 2:
The class "android.content.res.Resources", loaded by "", occupies 3,962,504 (19.02%) bytes. The memory is accumulated in one instance of "java.lang.Object[]" loaded by "".
Problem Suspect 3:
One instance of "android.graphics.Bitmap" loaded by "" occupies 3,145,792 (15.10%) bytes. The memory is accumulated in one instance of "byte[]" loaded by "".
Judging from the reports its obvious that memory leak is because of bitmap. I've researched a lot but couldn't rectify this leak. Please help me out. I'm using ImageLoader class to download and display bitmaps. To use this class I simply call the displayImage() method. Here is the code:
public class ImageLoader {
private static ImageLoader imageLoader;
private int maxNoOfConnections = 4;
FileCache fileCache;
ExecutorService executorService;
HttpURLConnection conn;
InputStream is;
OutputStream os;
PhotosLoader photosLoader;
Handler handler;
Bitmap bitmap;
private ImageLoader(Context context) {
fileCache = new FileCache(context);
executorService = Executors.newFixedThreadPool(maxNoOfConnections);
handler = new Handler();
}
public static ImageLoader getInstance(Context context) {
if (imageLoader == null)
imageLoader = new ImageLoader(context);
return imageLoader;
}
public void displayImage(String url, ProgressBar pBar, ImageView imageView) {
photosLoader = new PhotosLoader(url, imageView, pBar);
executorService.submit(photosLoader);
}
private Bitmap getBitmap(String url) {
File f = fileCache.getFile(url);
bitmap = decodeFile(f);
if (bitmap != null)
return bitmap;
try
{
URL imageUrl = new URL(url);
conn = (HttpURLConnection) imageUrl.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
is = conn.getInputStream();
os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex)
{
Log.e("inNews", "Image Url Malformed");
return null;
}
}
private Bitmap decodeFile(File f) {
try
{
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
final int REQUIRED_SIZE = 70;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true)
{
if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e)
{
}
return null;
}
class PhotosLoader implements Runnable {
String url;
ImageView imageView;
ProgressBar pBar;
Bitmap bmp;
public PhotosLoader(String url, ImageView imageView, ProgressBar pBar) {
this.url = url;
this.imageView = imageView;
this.pBar = pBar;
}
#Override
public void run() {
bmp = getBitmap(url);
handler.post(new Runnable() {
#Override
public void run() {
if (bmp != null)
{
pBar.setVisibility(View.GONE);
imageView.setImageBitmap(bmp);
} else
{
pBar.setVisibility(View.GONE);
imageView.setImageResource(R.drawable.img_no_image_grid);
}
}
});
}
}
}
Please help me rectify my code. Thanks!
Note: I've not used bitmap.recycle() since, the documentation says that post-Honeycomb the GC collects bitmaps and its no longer necessary to forcefully recycle it !
Memory leak problem always Java's problems.
I understand your code, code simple imagecache tool. Check SampleSize value and executor service run on only one thread. Four Thread have big memory and this background thread action. Your "handler" exchange to "runOnUIThread"
You should use;
Activity activity = (Activity)imageView.getContext();
__activity__.runOnUIThread(new Runnable()
{
if (bmp != null)
{
pBar.setVisibility(View.GONE);
imageView.setImageBitmap(bmp);
} else
{
pBar.setVisibility(View.GONE);
imageView.setImageResource(R.drawable.img_no_image_grid);
}
});
I think the problem is the Singleton instance... I made a fork of the LazyList project, check this:
https://github.com/nicolasjafelle/LazyList
I have the same Memory Leak but, and maybe I am wrong, this singleton will never be garbage collected unless you kill the process with System.exit().
That is why the original LazyList project not use Singleton. I also think that if you need a cache you will need to be fast and the same for all the application.
The important thing in this ImageLoader is the FileCache and MemoryCache, when you call clearCache the bitmaps where collected.
I think the answer is simple, just keep clearing cache when you dont need memory/when OOM exception arises. I did that for you
MemoryCache memoryCache = new MemoryCache();
try {
Bitmap bitmap = null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) imageUrl
.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is = conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
conn.disconnect();
bitmap = decodeFile(f);
return bitmap;
} catch (Throwable ex) {
ex.printStackTrace();
if (ex instanceof OutOfMemoryError)
memoryCache.clear();
return null;
}
when you have to initialise a library in an activity, always pass the application context, not the activity context.

Android saving large bitmaps

I'm new to android and I'm confused about how to deal with Bitmaps.
I want to download a Bitmap, it could be quite large, and save it to a temporary internal file. I'm then going to draw this Bitmap to a Canvas later.
My current method is to
1. Download the input stream
2. copy the stream
3. use one stream to work out bounds using bitmapFactory.options
4. use the other stream to decode the full bitmap with the sample size
However, I need landscape and portrait versions, so now I will have to do this twice and save two images.
Or - I have seen people use bm.compress(Bitmap.CompressFormat.JPEG, 50, bos); to save a file instead. This by-passes the decoding with sample size as its saved direct from a stream. I guess then I would use a matrix to scale when I draw to my Canvas.
Basically, I am confused as the best approach for this task , which method is less likely to run into out of memory and is the more commonly used approach?
Cheers
byte[] imagesByte = getLogoImage(Your url);
set to imageview...
imgView.setImageBitmap(BitmapFactory.decodeByteArray( imagesByte, 0, imagesByte.length));
Method for Download
public static byte[] getLogoImage(String url){
try {
URL imageUrl = new URL(url);
URLConnection ucon = imageUrl.openConnection();
InputStream is = ucon.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
ByteArrayBuffer baf = new ByteArrayBuffer(500);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte) current);
}
return baf.toByteArray();
} catch (Exception e) {
Log.d("ImageManager", "Error: " + e.toString());
}
return null;
}
In Android you have to e aware of limited memory, so large images would't fit in memory and you will have OutOfMemory exceptions.
The key is, after saving te image in internal storage, load it at the display resolution:
First download te image, this should be done outside the UI thread, let _url an URL intance with the image addres and _file the String containing destination file :
URLConnection conn = _url.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
boolean success = false; //track succesful operation
if( _file != null)
{
try
{
FileOutputStream fos = new FileOutputStream(_file);
byte data[] = new byte[4086]; //use 4086 bytes buffer
int count = 0;
while ((count = is.read(data)) != -1)
{
fos.write(data, 0, count);//write de data
}
is.close();
fos.flush();
fos.close();
int len = conn.getContentLength();
File f = new File( _file);//check fie length is correct
if( len== f.length())
{
success = true;
}
else
{
//error downloading, delete de file
File tmp = new File( _file);
if( tmp.exists())
{
tmp.delete();
}
}
}catch (Exception e )
{
try
{
e.printStackTrace();
//delete file with errors
File tmp = new File( _file);
if( tmp.exists())
{
tmp.delete();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
finally
{
is.close();//cleanup
}
Then when you have to load the image at the desired resolution, here the key is use BitmapFactory to read bitmap info and get scaled bitmap:
public static Bitmap bitmapFromFile(int width, int height, String file)
{
Bitmap bitmap = null;
final BitmapFactory.Options options = new BitmapFactory.Options();
if( height >0 && width > 0 ) {
options.inJustDecodeBounds = true;//only read bitmap metadata
BitmapFactory.decodeFile(file,options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, width, height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
}
try
{
bitmap = BitmapFactory.decodeFile(file, options);//decode scaled bitmap
}catch (Throwable t)
{
if( bitmap != null)
{
bitmap.recycle();//cleanup memory, very important!
}
return null;
}
return bitmap
}
The final step is to calculate the scale factor:
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;
final int halfWidth = width;
// 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 ((couldShrink(halfWidth, reqWidth, inSampleSize)&&
couldShrink(halfHeight,reqHeight, inSampleSize))
//&&(halfHeight*halfWidth)/inSampleSize > maxsize)
)
{
inSampleSize *= 2;
}
}
return inSampleSize;
}
private static boolean couldShrink ( int dimension, int req_dimension, int divider)
{
int actual = dimension / divider;
int next = dimension / (divider*2);
int next_error = Math.abs(next - req_dimension);
int actual_error = Math.abs(actual-req_dimension);
return next > req_dimension ||
(actual > req_dimension && (next_error < actual_error) )
;
}
That is if you want to do it by hand, I recommend you to use Picasso that will handle donwloading, disk caching and memory caching of your image:
To load into a ImageView called image showing a backgroud (R.drawable.img_bg) while downloading :
Picasso.with(image.getContext())
.load(url).placeholder(R.drawable.img_bg).fit()
.into(image, new Callback.EmptyCallback()
{
#Override
public void onSuccess()
{
holder.progress.setVisibility(View.GONE); //hide progress bar
}
#Override
public void onError()
{
holder.progress.setVisibility(View.GONE); //hide progress bar
//do whatever you design to show error
}
});
to handle yourself a bitmap:
//first declare a target
_target = new Target()
{
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from)
{
//handle your bitmap (store it and use it on you canvas
}
#Override
public void onBitmapFailed(Drawable errorDrawable)
{
//handle your fail state
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable)
{//for example for drawing a placeholder while downloading
}
};
Now you just have to load and resize your image:
Picasso.with(context).load(url).resize(192, 192).centerCrop().into(_target);
Hope that helps.

app is slow with images in android

I am doing a app on images to show them in GridView, i am fetching 20 images from server. Resolution of the each image is 720*540.I used JSON parsing to fetch url and used below code to convert into Bitmap in order to set images.
public static Bitmap loadImageFromUrl(String url) {
InputStream inputStream;Bitmap b;
try {
inputStream = (InputStream) new URL(url).getContent();
BitmapFactory.Options bpo= new BitmapFactory.Options();
if(bpo.outWidth>500) {
bpo.inSampleSize=8;
b=BitmapFactory.decodeStream(inputStream, null,bpo );
} else {
bpo.inSampleSize=2;
b=BitmapFactory.decodeStream(inputStream, null,bpo );
}
return b;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
my app is working fine but it is taking too much time to load the images. So that my app became slow. Should i decrease the resolution of images?
how to come out of the issue?
If you are doing a grid view to load 20 images of such resolution, I would suggest the following:
Definitely reduce the size of the images. Unless you are targeting a tablet, you will be fine as most smartphones cannot achieve that resolution with 20 images.
Cache images if you can.
Download the images on a different thread. Store a HashMap would make it easy for you, just put all the imageviews with the image file names or other form of IDs as keys. send message to your Handler when images are downloaded and update the view after it's decoded. You can retrieve your views directly. Just remember to check if they are still in the window. This way the images will show up one after another quickly. I don't think multithreading the images will help, just make sure to use another thread to "push the images" and the main UI thread updates. User experience will be greatly improved then.
Hope this helps.
---some implementations, I don't have the complete code with me right now---
Have a data structure to match the views with data that comes in. very handy here.
private HashMap<String,ImageView> pictures;
When you get the list of image urls, iterate through them:
pictures.put(id,view);
try{
FileInputStream in = openFileInput(id);
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeStream(in, null, null);
view.setImageBitmap(bitmap);
}catch(Exception e){
new Thread(new PictureGetter(this,mHandler,id)).start();
}
(Here the picture getter will simply fetch the image if it is not cached already and cache it)
Code to update the image view:
if(id!=null){
ImageView iv = pictures.get(id);
if(iv!=null){
try{
FileInputStream in = openFileInput(id);
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeStream(in, null, null);
iv.setImageBitmap(bitmap);
}catch(Exception e){
}
}
Picasso library
Solution is instead of using bitmap to load image directly use a awesome Library called Picasso its just super fast i know you really love this you can do this like this
Add picasso jar file to your project (Download picasso jar file here) Use picasso to load the Image like this
Picasso.with(context).load(new File(title)).centerCrop()
.resize(150, 150).error(R.drawable.ic_launcher).into(image);
where title is the image path which you want to load. Crop,resize, error are optional.
I'm guessing that most of the loading time is because of the large amount of images combined with the size of the images.
There are 2 possible solutions:
Resize the images, or lower the quality of the images so that the filesize is below 75kb or so.
Use multi-threading to retrieve multiple images at once. This might not help if the user's connection is really slow, but if you combine this with a small enough filesize it might just help out enough. You might want to determine what the current bandwidth of the device is and base the number of threads you run on that.
For instance: 20 images of 75KB each and an available connection of 200 KB/s = 3 or 4 concurrent threads.
Hope this helps.
I have same problem in my android app. When you decode a bitmap from a big sized image and set as imageBitmap to an image view probably your application will slow and after a few try you'll get an "out of memory exception"
Two of the possible ways you can try to handle this problem:
1- Reduce bitmap size when you decode from file
2- Use an image library.
I prefered second way and used Universal Image Loader. https://github.com/nostra13/Android-Universal-Image-Loader
String url = "file://" + your_file_path
com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(url, ivPicture, options);
public class clothImageLoader {
// the simplest in-memory cache implementation. This should be replaced with
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
// public static HashMap<String, Bitmap> cache = new HashMap<String,
// Bitmap>();
private static File cacheDir;
public clothImageLoader(Context context) {
// Make the background thead low priority. This way it will not affect
// the UI performance
photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);
// Find the dir to save cached images
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
// cacheDir=new
// File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
cacheDir = new File(ConstValue.MY_ClothBitmap_DIR);
else
cacheDir = context.getCacheDir();
if (!cacheDir.exists())
cacheDir.mkdirs();
}
final int stub_id = R.drawable.icon;
public void DisplayImage(String url, Activity activity, ImageView imageView) {
if (ConstValue.ClothRoomcache.containsKey(url))
imageView.setImageBitmap(ConstValue.ClothRoomcache.get(url));
else {
queuePhoto(url, activity, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, Activity activity, ImageView imageView) {
// This ImageView may be used for other images before. So there may be
// some old tasks in the queue. We need to discard them.
photosQueue.Clean(imageView);
PhotoToLoad p = new PhotoToLoad(url, imageView);
synchronized (photosQueue.photosToLoad) {
photosQueue.photosToLoad.push(p);
photosQueue.photosToLoad.notifyAll();
}
// start thread if it's not started yet
if (photoLoaderThread.getState() == Thread.State.NEW)
photoLoaderThread.start();
}
private Bitmap getBitmap(String url) {
// I identify images by hashcode. Not a perfect solution, good for the
// demo.
String filename = String.valueOf(url.hashCode());
File f = new File(cacheDir, filename);
// from SD cache
Bitmap b = decodeFile(f);
if (b != null)
return b;
// from web
try {
Bitmap bitmap = null;
/*
* InputStream is=new URL(url).openStream(); OutputStream os = new
* FileOutputStream(f); Utils.CopyStream(is, os); os.close();
*/
URL url1 = new URL(url);
bitmap = decodeFile(f);
/* Open a connection to that URL. */
URLConnection ucon = url1.openConnection();
/*
* Define InputStreams to read from the URLConnection.
*/
InputStream is = ucon.getInputStream();
// FlushedInputStream a = new FlushedInputStream(is);
BufferedInputStream bis = new BufferedInputStream(is);
/*
* Read bytes to the Buffer until there is nothing more to read(-1).
*/
ByteArrayBuffer baf = new ByteArrayBuffer(5000);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte) current);
}
/* Convert the Bytes read to a String. */
FileOutputStream fos = new FileOutputStream(f);
fos.write(baf.toByteArray());
fos.flush();
fos.close();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
// decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
try {
// decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = ConstValue.bmpSize;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale++;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {
}
return null;
}
// Task for the queue
private class PhotoToLoad {
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i) {
url = u;
imageView = i;
}
}
PhotosQueue photosQueue = new PhotosQueue();
public void stopThread() {
photoLoaderThread.interrupt();
}
// stores list of photos to download
class PhotosQueue {
private Stack<PhotoToLoad> photosToLoad = new Stack<PhotoToLoad>();
// removes all instances of this ImageView
public void Clean(ImageView image) {
for (int j = 0; j < photosToLoad.size();) {
if (photosToLoad.get(j).imageView == image)
photosToLoad.remove(j);
else
++j;
}
}
}
class PhotosLoader extends Thread {
public void run() {
try {
while (true) {
// thread waits until there are any images to load in the
// queue
if (photosQueue.photosToLoad.size() == 0)
synchronized (photosQueue.photosToLoad) {
photosQueue.photosToLoad.wait();
}
if (photosQueue.photosToLoad.size() != 0) {
PhotoToLoad photoToLoad;
synchronized (photosQueue.photosToLoad) {
photoToLoad = photosQueue.photosToLoad.pop();
// photoToLoad=photosQueue.photosToLoad.get(0);
// photosQueue.photosToLoad.remove(photoToLoad);
}
Bitmap bmp = getBitmap(photoToLoad.url);
ConstValue.ClothRoomcache.put(photoToLoad.url, bmp);
if (((String) photoToLoad.imageView.getTag()).equals(photoToLoad.url)) {
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad.imageView);
Activity a = (Activity) photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
if (Thread.interrupted())
break;
}
} catch (InterruptedException e) {
// allow thread to exit
}
}
}
PhotosLoader photoLoaderThread = new PhotosLoader();
// Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable {
Bitmap bitmap;
ImageView imageView;
public BitmapDisplayer(Bitmap b, ImageView i) {
bitmap = b;
imageView = i;
}
public void run() {
if (bitmap != null)
imageView.setImageBitmap(bitmap);
else
imageView.setImageResource(stub_id);
}
}
public static void clearCache() {
// clear memory cache
ConstValue.ClothRoomcache.clear();
// clear SD cache
File[] files = cacheDir.listFiles();
for (File f : files)
f.delete();
}
public class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
#Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int a = read();
if (a < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
}
when you call the method ,in the gridView getView method:
holder.image.setTag(ChoseInfo.get(position).getLink());
imageLoader.DisplayImage(ChoseInfo.get(position).getLink(), activity, holder.image);
ChoseInfo.get(position).getLink())
Here getLink() is internet link.

How to use cache memory for image download?

I am displaying image from web using Image downloading logic,I want to image download at first time only,next time don't download image from web,because First time downloaded image store in cache memory so i display image from catch memory,In case,does not exits that image in cache memory,will download otherwise don't need download that image from web,How is possible?
Thanks Friends.
If you'd rather not reinvent the wheel here, you can either use droid-fu's image loading with caching built in, or dig deeper into its cachefu classes for more. Particularly, AbstractCache is a good base for a two-level cache; in this case, it keeps a smaller in-memory cache and if an SD card is available it'll keep extras there.
I am not an android dev but I believe there should be a way to write to the local memory. I.e write to a directory. I guess the image comes back from the web as an array of bytes which you can save to the local mem. Then you can probably just read it back whenever you need it again.
You can implement a "CacheManager" singleton class with a Hashtable cache, so when your download finish add to the cache object doing cache.put(imageUrl, imageView). Have to do this in a singleton to mantain the cache in the application lifecycle.
Here is a link to an image caching class.
http://theandroidcoder.com/utilities/android-image-download-and-caching/
It seems to work nicely and supports both memory and sdcard caching
Below the imageLoader class to maintain cache memory and Disk memory to image store in disk after download the images.
public class MyImageLoader {
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "ImageCache";
private DiskLruImageCache mDiskLruImageCache;
private ExecutorService executorService;
private LruCache<String, Bitmap> mMemoryCache;
private Map<ImageView, String> imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
private int byteCounts;
private int requiredHeight = 100, requiredWidth = 100; // setting default height & width as 100
private final int default_icon = R.drawable.no_image_friend;
CommonMethod mCommonMethod;
public MyImageLoader(Context context) {
executorService = Executors.newFixedThreadPool(2);
final int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
mCommonMethod = new CommonMethod(context);
mDiskLruImageCache = new DiskLruImageCache(context, DISK_CACHE_SUBDIR, DISK_CACHE_SIZE, CompressFormat.PNG, 70);
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap bitmap) {
byteCounts = bitmap.getRowBytes() * bitmap.getHeight();
return byteCounts;
}
};
}
public void ExecuteLoading(String urlString, ImageView mImageView) {
imageViews.put(mImageView, urlString);
Bitmap bitmap = getBitmapFromMemCache(urlString);
if (bitmap != null){
mImageView.setImageBitmap(bitmap);
}
else {
executorService.submit(new LoadImages(urlString, mImageView));
mImageView.setImageResource(default_icon);
}
}
boolean ImageViewReused(String urlString, ImageView mImageView){
String tag=imageViews.get(mImageView);
if(tag==null || !tag.equals(urlString))
return true;
return false;
}
class LoadImages implements Runnable {
String urlString;
ImageView mImageView;
DisplayImages images;
public LoadImages(String urlString, ImageView mImageView) {
this.urlString = urlString;
this.mImageView = mImageView;
}
public void run() {
if(!ImageViewReused(urlString, mImageView)){
Bitmap bitmap = DownloadFromUrl(urlString);
Bitmap mBitmapMask = mCommonMethod.makeMaskImageCrop(bitmap, R.drawable.image_thumb_mask, R.drawable.image_thumb);
//TODO to mask image then bitmap pass
addBitmapToDiskCache(urlString, mBitmapMask);
DisplayImages images = new DisplayImages(urlString, mImageView, mBitmapMask);
((Activity) mImageView.getContext()).runOnUiThread(images);
}
}
}
class DisplayImages implements Runnable {
Bitmap bitmap;
String urlString;
ImageView mImageView;
public DisplayImages(String urlString, ImageView mImageView, Bitmap bitmap) {
this.urlString = urlString;
this.mImageView = mImageView;
this.bitmap = bitmap;
}
public void run() {
if(!ImageViewReused(urlString, mImageView)){
if (bitmap != null)
mImageView.setImageBitmap(bitmap);
else
mImageView.setImageResource(default_icon);
}
}
}
private Bitmap DownloadFromUrl(String urlString) {
return decodeBitmapFromStream(urlString, getReqiredWidth(), getRequiredHeight());
}
private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
synchronized (mMemoryCache) {
if (mMemoryCache.get(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
}
private Bitmap getBitmapFromMemCache(String key) {
Bitmap bitmap = mMemoryCache.get(key);
if(bitmap == null){
bitmap = getBitmapFromDiskCache(key);
}
return bitmap;
}
private void addBitmapToDiskCache(String key, Bitmap bitmap) {
synchronized (mDiskLruImageCache) {
if (!mDiskLruImageCache.containsKey(String.valueOf(key.hashCode()))) {
mDiskLruImageCache.put(String.valueOf(key.hashCode()), bitmap);
addBitmapToMemoryCache(key, bitmap);
}
}
}
private Bitmap getBitmapFromDiskCache(String key) {
return mDiskLruImageCache.getBitmap(String.valueOf(key.hashCode()));
}
private 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;
inSampleSize = Math.min(width/reqWidth, height/reqHeight);
return inSampleSize;
}
private static Bitmap decodeBitmapFromStream(String urlString, int reqWidth, int reqHeight) {
URL url = null;
InputStream is = null;
try {
url = new URL(urlString);
is = (InputStream) url.getContent();
} catch (Exception e) {
e.printStackTrace();
}
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// As InputStream can be used only once we have to regenerate it again.
try {
is = (InputStream) url.getContent();
} catch (IOException e) {
e.printStackTrace();
}
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(is, null, options);
}
public int getRequiredHeight() {
return requiredHeight;
}
public void setRequiredHeight(int longest, int requiredHeight) {
this.requiredHeight = requiredHeight > longest ? longest : requiredHeight;
}
public int getReqiredWidth() {
return requiredWidth;
}
public void setReqiredWidth(int longest, int requiredWidth) {
this.requiredWidth = requiredWidth > longest ? longest : requiredWidth;
}
public void clearCacheMemory() {
if(mMemoryCache.size() > 0){
mMemoryCache.evictAll();
}
}
public void clearDiskMemory() {
mDiskLruImageCache.clearCache();
}
}
hope you will get some idea and hint from the above code..

Categories

Resources