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);
}
Related
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).
So I'm fairly new to Android dev, and I've run into an oddity with using ImageViews. Any pointers or suggestions would be very much welcomed!
I'm setting the bitmaps for my ImageViews dynamically, which is sorta working. Except for that they only sometimes display the image, the rest of the time I get a full color fill with the approximate image color as seen below.
I think they are being scaled properly using this code I got from Android forums, so I don't think I'm running into memory issues....
public static Bitmap decodeSampledBitmapFromStream(InputStream inputStream, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream,null,options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(inputStream,null,options);
}
After many hours of working over the issue, I finally figured out that since I was using an AsyncTask to facilitate the loading of these bitmaps, they were occasionally faster than the main UI thread. This screwed me up when I called myImageView.getHeight() & width().
So here's the solution that I came up with in the hope that it might help someone else along the way:
public class DecodeTask extends AsyncTask<String, Void, Bitmap> {
public ImageView currentImage;
private static AssetManager mManager;
public DecodeTask(ImageView iv, AssetManager inputManager) {
currentImage = iv;
mManager = inputManager;
}
protected Bitmap doInBackground(String... params) {
int bottomOut = 1000;
while(currentImage.getMeasuredWidth() == 0 && currentImage.getMeasuredHeight() == 0 && (--bottomOut) > 0)
{
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
InputStream iStream = null;
Bitmap bitmap = null;
try {
iStream = mManager.open("photos" + File.separator + params[0]);
bitmap = ImageUtils.decodeSampledBitmapFromStream(iStream, currentImage.getMeasuredWidth(), currentImage.getMeasuredHeight());
iStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
#Override
protected void onPostExecute(Bitmap result) {
if(currentImage != null) {
currentImage.setImageBitmap(result);
currentImage.invalidate();
}
}
}
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.
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.
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..