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..
Related
I am working with an application and have about 10 Recycle view, when i move between fragments, app crash with out of memory.
I am using a lot of images in this app
I want to know how to apply bitmap recycle as it's the main reason of the exception
My recycle adapter is:
public void onBindViewHolder(MboViewHolder holder, int position) {
GameEvent gameEvent = ev.get(position);
holder.bindPhoto(holder,cnt,gameEvent.getEventImage());}
BindPhoto mwthod is:
public void bindPhoto(MboViewHolder mbo,Context cnt, String photoUrl) {
mbo.img.setTag(photoUrl);
Bitmap imgz = Tools.getPhoto(photoUrl, 0);
if (imgz != null) {
mbo.img.setImageBitmap(imgz);
Log.e("NoDwnLd","No");
} else {
Bitmap largeIcon = BitmapFactory.decodeResource(cnt.getResources(), R.drawable.ic_default);
mbo.img.setImageBitmap(largeIcon);
new DownloadBitmap(cnt,mbo.img,"2").execute(photoUrl);
}
My DownloadBitmap asynctask is:
public class DownloadBitmap extends AsyncTask<String, Void, Bitmap> {
private int flag=0;
private ImageView img;
private String type;
private HashMap<String, Bitmap> map= new HashMap<>();
private Context cnt;
private String url;
public DownloadBitmap(Context cnt, ImageView img, String type) {
this.cnt = cnt;
this.img=img;
this.type=type;
}
public DownloadBitmap(Context cnt, ImageView img, String type, HashMap<String, Bitmap> map) {
this.cnt = cnt;
this.img=img;
this.type=type;
this.map=map;
}
public DownloadBitmap(Context context) {
this.cnt=context;
this.flag=2;
}
#Override
protected Bitmap doInBackground(String... params) {
Bitmap bitmap=null;
if (cnt!=null){
boolean check = new CheckInternetConnection(cnt).haveNetworkConnection();
if (check) {
try {
url=params[0];
if (url==null || url.equals("")) return null;
InputStream in = new java.net.URL(url).openStream();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = Globals.inSampleSize;
bitmap = BitmapFactory.decodeStream(in,null,options);
return bitmap;
} catch (Exception e) {
Log.e("ImageDownload", "Download failed: " + e.getMessage());
}
}
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if(bitmap != null){
bitmap=Tools.resizeImage(bitmap,500,500);
//view.setImageViewBitmap(R.id.nt_img, bitmap);
if(type == "1") Tools.sendNotification(cnt, bitmap);
if(type == "2") {
if(img.getTag()!= null && img.getTag() == url){
// keep all images stored on memory for fast retrieval
// map.put(url, bitmap);
// Log.e("url", url);
// save the image inside the image holder
//img.setImageBitmap(map.get(url));
Log.e("DwnLD",img.getTag()+"");
img.setImageBitmap(bitmap);
Tools.storePhoto(img.getTag().toString(), bitmap);
}
// Log.e("ImageDownload", "bitmap in imageview");
}
if (type == null){
// map.put(url, bitmap);
// if (img!=null && map.get(url)!=null)img.setImageBitmap(map.get(url));
if (img!=null)img.setImageBitmap(bitmap);
}
if (cnt != null && flag ==2){
Tools.storePhoto(CreateEvent1Fragment.searchResult.get(0).getEventImage(),bitmap);
// Log.e("ImageDownload", "bitmap in imageview");
}
}
}
My Tools.resizeImage is:
public static Bitmap resizeImage(Bitmap bitmap,int newWidth,int newHeight){
Bitmap resized = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
return resized;
}
My Tools.storePhoto is:
public static void storePhoto(String url,Bitmap image){
File img = null;
File env = new File(Environment.getExternalStorageDirectory() + Globals.DIR);
if(!env.exists()) env.mkdir();
String filename = extractUrl(url);
img=new File(Environment.getExternalStorageDirectory()+Globals.DIR+filename);
if (!img.exists()) {
// Log.e("PHOTOS",img.getAbsolutePath());
try {
FileOutputStream fos = new FileOutputStream(img);
image.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
My Tools.getPhoto is:
public static Bitmap getPhoto(String url,int type){
Bitmap bmp=null;
String filename = extractUrl(url);
File ff = new File(Environment.getExternalStorageDirectory()+Globals.DIR+filename);
if(!ff.exists()){
return bmp;
}else {
if (type != 1){
bmp = Tools.decodeFile(ff);
return bmp;
}else {
bmp = BitmapFactory.decodeFile(ff.getAbsolutePath());
return bmp;
}
}
}
My Tools.decodeFile is:
public static Bitmap decodeFile(File f) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
// The new size we want to scale to
final int REQUIRED_SIZE=70;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while(o.outWidth / scale / 2 >= REQUIRED_SIZE &&
o.outHeight / scale / 2 >= REQUIRED_SIZE) {
scale *= 2;
}
o.inSampleSize = scale;
o.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o);
} catch (FileNotFoundException e) {}
return null;
}
I want to apply bitmap recycle... How can I do that?
try using Libs Glide
https://github.com/bumptech/glide
change
if (imgz != null) {
mbo.img.setImageBitmap(imgz);
Log.e("NoDwnLd","No");
} else {
Bitmap largeIcon = BitmapFactory.decodeResource(cnt.getResources(), R.drawable.ic_default);
mbo.img.setImageBitmap(largeIcon);
new DownloadBitmap(cnt,mbo.img,"2").execute(photoUrl);
}
to
if(!photoUrl.isEmpty()) {
Glide.with(this).load(photoUrl).error(R.drawable.ic_default).into(mbo.img);
Log.e("NoDwnLd","No");
} else {
Glide.with(this).load(R.drawable.ic_default).error(R.drawable.ic_default).into(mbo.img);
new DownloadBitmap(cnt,mbo.img,"2").execute(photoUrl);
}
Background
I have a RecyclerView (with GridLayoutManager) in my app which needs to show some sensitive encrypted images stored locally.
I cannot use something like this,
Glide.with(this).load(encryptedImageFile).into(imageView);
as I first need to decrypt them and then set the image.
What I Have Done
I have an AsyncTask which decrypts those small thumbnail images and gives me a resulting Bitmap to set in the ImageView.
Here are some small code snippets of the Adapter and the AsyncTask.
#Override
public void onBindViewHolder(MediaViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
EncryptedFileContainer encryptedFileContainer = getItem(position);
if (encryptedFileContainer != null) {
EncryptedFileDBModel encryptedFileDBModel = encryptedFileContainer.getEncryptedFileDBModel();
holder.mImage.setTag(encryptedFileDBModel.getOriginalFileName());
new BindEncryptedThumbnail(holder, encryptedFileContainer).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
The AsyncTask to decrypt and set Bitmaps to the ImageViews,
static class BindEncryptedThumbnail extends AsyncTask<Object, Void, Bitmap> {
private EncryptedFileContainer mEncryptedFileContainer;
private MediaViewHolder mHolder;
public BindEncryptedThumbnail(MediaViewHolder viewHolder, EncryptedFileContainer encryptedFileContainer) {
this.mHolder = viewHolder;
this.mEncryptedFileContainer = encryptedFileContainer;
}
protected Bitmap doInBackground(Object... objects) {
if (mEncryptedFileContainer.getEncryptedFile() != null) {
return mEncryptedFileContainer.getDecryptedThumb().getThumb(100);
} else {
return null;
}
}
protected void onPostExecute(Bitmap thumbnail) {
String name = (String) mHolder.mImage.getTag();
EncryptedFile encryptedFile = mEncryptedFileContainer.getEncryptedFile();
EncryptedFileDBModel encryptedFileDBModel = mEncryptedFileContainer.getEncryptedFileDBModel();
if (encryptedFile != null && StringUtils.equals(name, encryptedFileDBModel.getOriginalFileName())
&& (thumbnail != null) && (mHolder.mImage != null)) {
mHolder.mImage.setImageBitmap(thumbnail);
} else {
mHolder.mImage.setImageDrawable(ResourceUtils.getDrawable(R.drawable.img_placeholder));
}
}
}
The Problem
Everything works fine and images are decrypted properly and being set in the RecyclerView items correctly. But when there are lots of images the memory footprint of the app shoots high up and there are several GCs as there is no BitmapPool to efficiently load images.
I would not like to implement disk caching and BitmapPool by myself as these have been effectively done in Glide (FYI, I am using Glide in other parts of my app).
I would like to use Glide to load a Bitmap (returned by my AsyncTask) and set it into the ImageView so that I can decrypt my images and also make use of all the goodness Glide offers.
EDIT:
Adding more information on how I am generating the thumbnail.
public Bitmap getThumb(int thumbnailSize) {
if ((!mThumbnailCreated) && (mThumbnailBitmap == null)) {
CipherInputStream streamThumb = readStream();
this.mThumbnailBitmap = ThumbnailEncryptionFactory
.getThumbnailfromStream(streamThumb, thumbnailSize);
}
return mThumbnailBitmap;
}
and this,
public static Bitmap getThumbnailfromStream(CipherInputStream streamThumb, int size) {
if (streamThumb != null) {
try {
byte[] bytes = IOUtils.toByteArray(streamThumb);
return decodeSampledBitmapFromByte(bytes, size, size);
} catch (IOException e) {
Timber.d("Error decoding thumbnail.");
}
}
return null;
}
public static Bitmap decodeSampledBitmapFromByte(byte[] stream, int reqWidth, int reqHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(stream, 0, stream.length, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(stream, 0, stream.length, options);
}
I am working to add an image from the gallery horizontally using recycler view as follows. I could able to add images next to each other.
The following code works but crashes sometimes because of out of memory concerns.
RecyclerViewAdapter, I am calling setSelectedPic method to load the image.
#Override
public void onBindViewHolder(final ListViewHolder holder, int position) {
PostProductImageLIst item = mItems.get(position);
ImageUtil.setSelectedPic(holder.imgViewIcon, item.getPath());
}
ImageUtil Class
public static void setSelectedPic(CustomNetworkImageView view, String url) {
Context context = view.getContext();
ImageLoader imageLoader = CustomVolleyRequest.getInstance(context).getImageLoader();
if (!TextUtils.isEmpty(url)) {
if (url.startsWith("http")) {
imageLoader.get(url, ImageLoader.getImageListener(view,
R.drawable.image, android.R.drawable
.ic_dialog_alert));
view.setImageUrl(url, imageLoader);
} else {
try {
Uri uri = Uri.parse(url);
InputStream is = context.getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(is);
view.setLocalImageBitmap(bitmap);
if (is!=null) {
is.close();
}
} catch (Exception ex) {
Log.e("Image", ex.getMessage(), ex);
}
}
} else {
view.setImageUrl("", CustomVolleyRequest.getInstance(view.getContext()).getImageLoader());
}
}
And then I decide to resize the images to solve the aferomentioned issue, out of memory-.The following code works. BUT, when I scroll back to first image, that image shows blank or white, but when I delete the second image, then I could able to see first image. I could not able to figure out the root of the problem. I am using NetworkImageView.
RecyclerViewAdapter, I am calling setSelectedPic method to load the image.
#Override
public void onBindViewHolder(final ListViewHolder holder, int position) {
PostProductImageLIst item = mItems.get(position);
ImageUtil.setSelectedPic(holder.imgViewIcon, item.getPath());
}
ImageUtil Class
public static void setSelectedPic(CustomNetworkImageView view, String url) {
Context context = view.getContext();
ImageLoader imageLoader = CustomVolleyRequest.getInstance(context).getImageLoader();
if (!TextUtils.isEmpty(url)) {
if (url.startsWith("http")) {
imageLoader.get(url, ImageLoader.getImageListener(view,
R.drawable.image, android.R.drawable
.ic_dialog_alert));
view.setImageUrl(url, imageLoader);
} else {
try {
int targetW = view.getWidth();
int targetH = view.getHeight();
Uri uri = Uri.parse(url);
InputStream is = context.getContentResolver().openInputStream(uri);
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(url, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
Bitmap bitmap = BitmapFactory.decodeStream(is);
Matrix matrix = new Matrix();
matrix.postRotate(getImageOrientation(url));
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, true);
view.setLocalImageBitmap(rotatedBitmap);
if (is!=null) {
is.close();
} catch (Exception ex) {
Log.e("Image", ex.getMessage(), ex);
}
}
} else {
view.setImageUrl("", CustomVolleyRequest.getInstance(view.getContext()).getImageLoader());
}
}
You should scale your Bitmap and use caching for reducing loading time. Here on this repository I've put reference code for approaching image loading with scaling and caching.
I report here code to be used for setPic method. This essentially uses an LruCache for improving Bitmap loading while scrolling. This could be a good starting point. Notice also that Bitmap are scaled before putting them in cache.
private static final int WIDTH = 100;
private LruCache<String, Bitmap> mMemoryCache;
public void setPic(MyImageView view, String url) {
Context context = view.getContext();
if (!TextUtils.isEmpty(url)) {
if (url.startsWith("http")) {
view.setImageUrl(url, VolleyHandler.getInstance(context).getImageLoader());
} else {
try {
Uri uri = Uri.parse(url);
Bitmap bitmap = mMemoryCache.get(url);
if (bitmap == null) {
InputStream is = context.getContentResolver().openInputStream(uri);
bitmap = BitmapFactory.decodeStream(is);
Bitmap scaled = ImageUtils.getInstance().scaleBitmap(bitmap);
mMemoryCache.put(url, scaled);
if (is!=null) {
is.close();
}
}
view.setLocalImageBitmap(bitmap);
} catch (Exception ex) {
Log.e("Image", ex.getMessage(), ex);
}
}
} else {
view.setImageUrl("", VolleyHandler.getInstance(view.getContext()).getImageLoader());
}
}
public Bitmap scaleBitmap(Bitmap bitmap) {
int width = WIDTH;
int height = (WIDTH * bitmap.getHeight()) / bitmap.getWidth();
return Bitmap.createScaledBitmap(bitmap, width, height, false);
}
I suggest also to refer to Android Developers documentation here (for efficient Bitmap loading) and here (for Bitmap caching with Volley).
i am New On Android I want set image in Background Which is getting from Url ..I am using ImageLoader Class
please Help me Set Image view In Background..
This Is MY Image Loader Class
public class ImageLoader {
// the simplest in-memory cache implementation. This should be replaced with
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
private HashMap<String, Bitmap> cache = new HashMap<String, Bitmap>();
private File cacheDir;
static ArrayList<String> img_path = new ArrayList<String>();
static String sd_card_folder_name = "ImageLoader";
static int width;
public ImageLoader(Context context, Activity acc) {
// Make the background thead low priority. This way it will not affect
// the UI performance
photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);
DisplayMetrics displaymetrics = new DisplayMetrics();
acc.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
width = displaymetrics.widthPixels;
Log.d("width", "width = " + width);
cache.clear();
img_path = new ArrayList<String>();
// 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(), sd_card_folder_name);
else
cacheDir = context.getCacheDir();
if (!cacheDir.exists())
cacheDir.mkdirs();
}
final int stub_id = R.drawable.loader;
public void DisplayImage(String url, Activity activity, ImageView imageView) {
try {
String filename = String.valueOf(url.hashCode());
File[] file_array = cacheDir.listFiles();
for (int i = 0; i < file_array.length; i++) {
img_path.add(file_array[i].getName());
}
if (img_path.contains(filename)) {
imageView.setImageBitmap(BitmapFactory
.decodeFile("/mnt/sdcard/" + sd_card_folder_name + "/"
+ filename));
} else {
if (cache.containsKey(url)) {
imageView.setImageBitmap(cache.get(url));
} else {
queuePhoto(url, activity, imageView, filename);
imageView.setImageResource(stub_id);
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
private void queuePhoto(String url, Activity activity, ImageView imageView, String _name) {
// 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, _name);
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.
if (url != null && !(url.equals(""))) {
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();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
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=300;
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;
}
// Task for the queue
private class PhotoToLoad {
public String url;
public ImageView imageView;
public String _name;
public PhotoToLoad(String u, ImageView i, String n) {
url = u;
imageView = i;
_name = n;
}
}
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();
}
Bitmap bmp = getBitmap(photoToLoad.url);
cache.put(photoToLoad.url, bmp);
Object tag = photoToLoad.imageView.getTag();
String FileName = photoToLoad._name;
if (FileName != null
&& ((String) FileName).equals(photoToLoad._name)) {
BitmapDisplayer bd = new BitmapDisplayer(bmp,
photoToLoad.imageView, FileName);
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;
String file_name;
public BitmapDisplayer(Bitmap b, ImageView i, String _name) {
bitmap = b;
imageView = i;
file_name = _name;
}
public void run() {
if (bitmap != null) {
load_full_image(imageView, file_name, bitmap);
} else
imageView.setImageResource(stub_id);
}
}
private Runnable mMyRunnable = new Runnable() {
#Override
public void run() {
}
};
public void load_full_image(ImageView img, String _name, Bitmap btmp) {
img_path = new ArrayList<String>();
File[] file_array = cacheDir.listFiles();
for (int i = 0; i < file_array.length; i++) {
img_path.add(file_array[i].getName());
}
if (img_path.contains(_name)) {
img.setImageBitmap(BitmapFactory
.decodeFile("/mnt/sdcard/" + sd_card_folder_name + "/"
+ _name));
} else {
img.setImageBitmap(btmp);
}
}
public void clearCache() {
// clear memory cache
cache.clear();
// clear SD cache
File[] files = cacheDir.listFiles();
for (File f : files)
f.delete();
}
this is my Activity Where I Get Image In imagview
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.userprofile);
Button addfrnd=(Button)findViewById(R.id.addfrnd);
ImageView image = (ImageView) findViewById(R.id.imageView1);
UserModel user = (UserModel) getIntent().getSerializableExtra("User");
UserByIdModel tempuser = (UserByIdModel) getIntent().getSerializableExtra("UserById");
String UserId=String.valueOf(tempuser.getUser_Id());
String image_url=tempuser.getUser_Image();
int loader = R.drawable.loader;
ImageLoader imgLoader = new ImageLoader(getApplicationContext(),
UserByIdProfile.this);
image.setTag(image_url);
// whenever you want to load an image from url
// call DisplayImage function
// url - image url to load
// loader - loader image, will be displayed before getting image
// image - ImageView
imgLoader.DisplayImage(image_url, UserByIdProfile.this, image);
you have to change in load_full_image method of imageLoader class, like below
public class ImageLoader {
...
Context mContext;
...
public ImageLoader(Context context, Activity acc) {
...
mContext = context
....
}
public void load_full_image(ImageView img, String _name, Bitmap btmp) {
img_path = new ArrayList<String>();
File[] file_array = cacheDir.listFiles();
for (int i = 0; i < file_array.length; i++) {
img_path.add(file_array[i].getName());
}
if (img_path.contains(_name)) {
Drawable d = new BitmapDrawable(mContext.getResources(),BitmapFactory
.decodeFile("/mnt/sdcard/" + sd_card_folder_name + "/"
+ _name));
img.setBackground(d);
} else {
Drawable d = new BitmapDrawable(mContext.getResources(),btmp);
img.setBackground(d);
}
}
}
try this code may be it will work
You have created a instance of ImageLoader but have not Initialize ImageLoader. For this add this line of code in your constructor after imageLoader = ImageLoader.getInstance(); :
imageLoader.init(ImageLoaderConfiguration.createDefault(ctx));
You have not declared any option for your imageLoader. Modify your DisplayImageOptions code as below:
DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.image_loading) .showImageForEmptyUri(R.drawable.no_image) .showImageOnFail(R.drawable.image_failed) .cacheInMemory(true) .cacheOnDisc(true) .bitmapConfig(Bitmap.Config.RGB_565) .build();
[Use this project aims to provide a reusable instrument for asynchronous image loading, caching and displaying.][1]
Features
1)Multithread image loading (async or sync)
2)Wide customization of ImageLoader's configuration (thread executors, downloader, decoder, 3)memory and disk cache, display image options, etc.)
4)Many customization options for every display image call (stub images, caching switch, decoding options, Bitmap processing and displaying, etc.)
5)Image caching in memory and/or on disk (device's file system or SD card)
6)Listening loading process (including downloading progress)
*****Android 2.0+ support
[1]: https://github.com/nostra13/Android-Universal-Image-Loader
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.