I am using universal-image-loader library to load images, but when I call copy() on a loaded bitmap file in some cases I get OutOfMemoryError.
Here is my code:
ImageLoader.getInstance().loadImage(path, new ImageLoadingListener() {
#Override
public void onLoadingStarted(String arg0, View arg1) {
// TODO Auto-generated method stub
}
#Override
public void onLoadingFailed(String arg0, View arg1, FailReason arg2) {
// TODO Auto-generated method stub
}
#Override
public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) {
bm = arg2;
}
#Override
public void onLoadingCancelled(String arg0, View arg1) {
// TODO Auto-generated method stub
}
});
Bitmap bm2= bm.copy(Bitmap.Config.ARGB_8888, true); //where the crash happens
I need the second Bitmap not to be mutable so I can draw on it.
First of all try to find a little time to read good official documentation about bitmaps: Displaying Bitmaps Efficiently
It will give you understanding why and when java.lang.OutofMemoryError happens. And how to avoid it.
What about your question: see this article: Android: convert Immutable Bitmap into Mutable
But from API Level 11 only options.inMutable available to load the
file into a mutable bitmap.
So, if we are building application with API level less than 11, then
we have to find some other alternatives.
One alternative is creating another bitmap by copying the source
bitmap. mBitmap = mBitmap.copy(ARGB_8888 ,true);
But the will throw OutOfMemoryException if the source file is big.
Actually incase if we want to edit an original file, then we will face
this issue. We should be able to load at-least image into memory, but
most we can not allocate another copy into memory.
So, we have to save the decoded bytes into some where and clear
existing bitmap, then create a new mutable bitmap and load back the
saved bytes into bitmap again. Even to copy bytes we cannot create
another ByteBuffer inside the memory. In that case need to use
MappedByteBuffer that will allocate bytes inside a disk file.
Following code would explain clearly:
//this is the file going to use temporally to save the bytes.
File file = new File("/mnt/sdcard/sample/temp.txt");
file.getParentFile().mkdirs();
//Open an RandomAccessFile
/*Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
into AndroidManifest.xml file*/
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
// get the width and height of the source bitmap.
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, width*height*4);
bitmap.copyPixelsToBuffer(map);
//recycle the source bitmap, this will be no longer used.
bitmap.recycle();
//Create a new bitmap to load the bitmap again.
bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
map.position(0);
//load it back from temporary
bitmap.copyPixelsFromBuffer(map);
//close the temporary file and channel , then delete that also
channel.close();
randomAccessFile.close();
And here is sample code.
Be thankful that it happens on your device, and not only on your user's devices.
1) That's something you have to cope with, and react to appropriately. Display error message, or load lower resolution of the bitmap. Your app will run on many kinds of devices, each has different amount of memory.
2) Use important function Bitmap.recycle after each operation which makes your old bitmap redundant. This will immediately free memory for next work without waiting for GC to run, and possible out of memory errors.
You cant do much about the bitmap outofmemory error except ensuring that the bitmap you are copying or displaying is not much large. Fortunately universal imageloader has a feature to compress bitmap by changing the config. So give Bitmap.Config.RGG_565 a try. Its supposed to half the memory footprint of the bitmap. You can also request for large heap size. Another thing you can do is copy the scaled version of the bitmap.
As Illegel Argument said, you need to make sure that the Bitmap is not TOO large. Also, make sure that you are only loading one Bitmap at a time into memory.
You can dynamically scale the bitmap using BitmapFactory
Bitmap b = BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.length)
image.setImageBitmap(Bitmap.createScaledBitmap(b, 300, 300, false));
Download this code from the site
http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/
extract its ImageLoader , file cache , memory cache classes use them in your bitmap to do things wont create out of memory exception at all and will cache images and increase performance
Use this Code to fullfill your purpose
Make the following classes in your code and in last use imageloader to load url pass it url , imageview and drawable to show incase url is not returning any image
FileCache.java
public class FileCache {
private File cacheDir;
public FileCache(Context context){
//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");
else
cacheDir=context.getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
}
public File getFile(String url){
//I identify images by hashcode. Not a perfect solution, good for the demo.
String filename=String.valueOf(url.hashCode());
//Another possible solution (thanks to grantland)
//String filename = URLEncoder.encode(url);
File f = new File(cacheDir, filename);
return f;
}
public void clear(){
File[] files=cacheDir.listFiles();
if(files==null)
return;
for(File f:files)
f.delete();
}
}
MemoryCache.java
public class MemoryCache {
private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());
public Bitmap get(String id){
if(!cache.containsKey(id))
return null;
SoftReference<Bitmap> ref=cache.get(id);
return ref.get();
}
public void put(String id, Bitmap bitmap){
cache.put(id, new SoftReference<Bitmap>(bitmap));
}
public void clear() {
cache.clear();
}
}
Utils.java
public class Utils {
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){}
}
}
ImageLoader.java
public class ImageLoader {
MemoryCache memoryCache=new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
public ImageLoader(Context context){
fileCache=new FileCache(context);
executorService=Executors.newFixedThreadPool(5);
}
final int stub_id = R.drawable.no_image;
public void DisplayImage(String url, ImageView imageView)
{
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url);
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
{
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, ImageView imageView)
{
PhotoToLoad p=new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url)
{
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(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.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=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;
}
//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;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad){
this.photoToLoad=photoToLoad;
}
#Override
public void run() {
if(imageViewReused(photoToLoad))
return;
Bitmap bmp=getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if(imageViewReused(photoToLoad))
return;
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
boolean imageViewReused(PhotoToLoad photoToLoad){
String tag=imageViews.get(photoToLoad.imageView);
if(tag==null || !tag.equals(photoToLoad.url))
return true;
return false;
}
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
public void run()
{
if(imageViewReused(photoToLoad))
return;
if(bitmap!=null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
call this code where you want to cache or download or manage images
imageLoader.DisplayImage(song.get(CustomizedListView.KEY_THUMB_URL), thumb_image);
Related
Hi am creating an application which pulls resources from Foursquare and displays it on the android phone and am having problems with loading images. The app runs but for some reason it does not load on the phone it only shows me the loader image which I defined
My code are as follows:
public class PicAdapter extends BaseAdapter {
//use the default gallery background image
int defaultItemBackground;
//gallery context
private Context galleryContext;
//array to store bitmaps to display
private Bitmap[] imageBitmaps;
//placeholder bitmap for empty spaces in gallery
Bitmap placeholder;
//constructor
public PicAdapter(Context c) {
//instantiate context
galleryContext = c;
//create bitmap array
//imageBitmaps = new Bitmap[10];
//decode the placeholder image
//placeholder = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
}
//BaseAdapter methods
//return number of data items i.e. bitmap images
public int getCount() {
return PicturesURLlist.size();
}
//return item at specified position
public Object getItem(int position) {
return position;
}
//return item ID at specified position
public long getItemId(int position) {
return position;
}
//get view specifies layout and display options for each thumbnail in the gallery
public View getView(int position, View convertView, ViewGroup parent) {
//create the view
ImageView imageView = new ImageView(galleryContext);
//specify the bitmap at this position in the array
//imageView.setImageBitmap(imageBitmaps[position]);
ImageLoader imgloader = new ImageLoader(getApplicationContext());
int loader = R.drawable.ic_launcher;
HashMap<String, String> song = new HashMap<String, String>();
song = PicturesURLlist.get(position);
//Log.d("Links", song.get(picurl));
imgloader.DisplayImage(song.get(picurl), loader, imageView );
//imageView.setImageBitmap((imgloader.DisplayImage(song.get(picurl), loader, imageView)));
//set layout options
imageView.setLayoutParams(new Gallery.LayoutParams(400, 600));
//scale type within view area
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
//set default gallery item background
imageView.setBackgroundResource(defaultItemBackground);
//return the view
return imageView;
}
//custom methods for this app
//helper method to add a bitmap to the gallery when the user chooses one
}
My imagloader class
public class ImageLoader {
MemoryCache memoryCache=new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
public ImageLoader(Context context){
fileCache=new FileCache(context);
executorService=Executors.newFixedThreadPool(5);
}
int stub_id = R.drawable.ic_launcher;
public String DisplayImage(String url, int loader, ImageView imageView)
{
stub_id = loader;
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url);
Log.d("Image loader succes", url);
if(bitmap!=null){
imageView.setImageBitmap(bitmap);
Log.d("Image loader succes", "true");
}
else
{
queuePhoto(url, imageView);
imageView.setImageResource(loader);
Log.d("Image loader succes", "false");
}
return null;
}
private void queuePhoto(String url, ImageView imageView)
{
PhotoToLoad p=new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url)
{
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(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.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=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;
}
//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;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad){
this.photoToLoad=photoToLoad;
}
public void run() {
if(imageViewReused(photoToLoad))
return;
Bitmap bmp=getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if(imageViewReused(photoToLoad))
return;
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
boolean imageViewReused(PhotoToLoad photoToLoad){
String tag=imageViews.get(photoToLoad.imageView);
if(tag==null || !tag.equals(photoToLoad.url))
return true;
return false;
}
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
public void run()
{
if(imageViewReused(photoToLoad))
return;
if(bitmap!=null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
I've searched a lot but I did not understand where to is my error.
First in my app I am getting images from the web if there is no net I am getting them from created database.
I am going to post my ImageLoader class then the memory class then the utils class if there is something wrong please I need some help Thanks in advance.
public class ClassImageLoader {
ClassMemoryCache memoryCache=new ClassMemoryCache();
ClassFileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
public ClassImageLoader(Context context){
fileCache=new ClassFileCache(context);
executorService=Executors.newFixedThreadPool(5);
}
final int stub_id=R.drawable.restlogobutton;
public void DisplayImage(String url, ImageView imageView)
{
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url);
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
{
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
private void queuePhoto(String url, ImageView imageView)
{
PhotoToLoad p=new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url)
{
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(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
ClassUtils.CopyStream(is, os);
os.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=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;
}
//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;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad){
this.photoToLoad=photoToLoad;
}
public void run() {
if(imageViewReused(photoToLoad))
return;
Bitmap bmp=getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if(imageViewReused(photoToLoad))
return;
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
boolean imageViewReused(PhotoToLoad photoToLoad){
String tag=imageViews.get(photoToLoad.imageView);
if(tag==null || !tag.equals(photoToLoad.url))
return true;
return false;
}
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
public void run()
{
if(imageViewReused(photoToLoad))
return;
if(bitmap!=null)
photoToLoad.imageView.setImageBitmap(bitmap);
else
photoToLoad.imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
public class ClassMemoryCache {
private Map<String, Bitmap> cache=Collections.synchronizedMap(
new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
private long size=0;//current allocated size
private long limit=1000000;//max memory in bytes
public ClassMemoryCache(){
//use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory()/4);
}
public void setLimit(long new_limit){
limit=new_limit;
}
public Bitmap get(String id){
if(!cache.containsKey(id))
return null;
return cache.get(id);
}
public void put(String id, Bitmap bitmap){
try{
if(cache.containsKey(id))
size-=getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size+=getSizeInBytes(bitmap);
checkSize();
}catch(Throwable th){
th.printStackTrace();
}
}
private void checkSize() {
if(size>limit){
Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated
while(iter.hasNext()){
Entry<String, Bitmap> entry=iter.next();
size-=getSizeInBytes(entry.getValue());
iter.remove();
if(size<=limit)
break;
}
}
}
public void clear() {
cache.clear();
}
long getSizeInBytes(Bitmap bitmap) {
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
public class ClassUtils {
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){}
}
}
memory management is a pretty extensive and advanced topic but I'll post a very important tip all in capital letters:
DO NOT USE MAP<k,v> !!!
the map is keeping references to the imageview (which keeps the activity context) and the bitmaps for ever and it's one of the main reasons you have those memory losses. Further on the reference to context is keep your whole activity in memory, for ever. All very bad ideas.
You'll use a LruCache (available on the compatibility library) to cache the bitmaps, and let only the activity keep reference to the imageviews (either static from the XML or dynamically using an adapter)
here is a Google IO video http://www.youtube.com/watch?v=gbQb1PVjfqM where the guys from Google themselves are showing some best practices around this area, on the 4min mark they show the usage of the LruCache.
// replace this methods in your code and try I am not sure it will solve your error but once you can try.
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();
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=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;
}
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inTempStorage = new byte[32*1024];
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
check this link for the ImageLoaderClass:
http://code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/lazylist/ImageLoader.java
OOM issue check this link:
Strange out of memory issue while loading an image to a Bitmap object
And if you still get an error then check this one link that I have found after many search and has solved my Problem:
https://groups.google.com/forum/?fromgroups=#!topic/android-developers/vYWKY1Y6bUo
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Android Out of Memory error with Lazy Load images
Outofmemory error during scrolling the listview fastly
I am using listview to display the images,names from the facebook api.I can able to display images correctly.My problem is when I scroll the list fastly I am getting
11-15 18:41:29.841: E/dalvikvm-heap(32303): 786432-byte external allocation too large for this process.
11-15 18:41:29.887: I/dalvikvm-heap(32303): Clamp target GC heap from 32.095MB to 32.000MB
11-15 18:41:29.887: E/GraphicsJNI(32303): VM won't let us allocate 786432 bytes
11-15 18:41:29.887: D/dalvikvm(32303): GC_FOR_MALLOC freed 0K, 51% free 4380K/8903K, external 23826K/25864K, paused 31ms
11-15 18:41:29.887: D/skia(32303): --- decoder->decode returned false
11-15 18:41:29.887: W/dalvikvm(32303): threadid=18: thread exiting with uncaught exception (group=0x40018560)
11-15 18:41:29.887: E/AndroidRuntime(32303): FATAL EXCEPTION: Thread-29
11-15 18:41:29.887: E/AndroidRuntime(32303): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-15 18:41:29.887: E/AndroidRuntime(32303): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
11-15 18:41:29.887: E/AndroidRuntime(32303): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
11-15 18:41:29.887: E/AndroidRuntime(32303): at com.beerbro.utils.ImageLoader.decodeFile(ImageLoader.java:156)
11-15 18:41:29.887: E/AndroidRuntime(32303): at com.beerbro.utils.ImageLoader.getBitmap(ImageLoader.java:99)
11-15 18:41:29.887: E/AndroidRuntime(32303): at com.beerbro.utils.ImageLoader$PhotosLoader.run(ImageLoader.java:214)
But when I scroll this listview slowly I am not getting any errors.
Here is my Adapter class where I called Imageloader class displayImage method to convert my url into bitmap and set it to imageview.
public class MySimpleArrayAdapter extends ArrayAdapter<String> {
private Activity context;
ArrayList<String> namear,msgar,idar,profimage,postimage,commentsnum,objectid,urlString;
ImageLoader imageLoader;
Bitmap[] bitdata;
public MySimpleArrayAdapter(Activity c,int i,ArrayList<String> postpic, ArrayList<String> names,ArrayList<String> msg,ArrayList<String> id,ArrayList<String> proimg,Bitmap[] bit,ArrayList<String> comment,ArrayList<String> objid,ArrayList<String> web) {
super(c, i, names);
Log.e("adapter","adap");
this.context = c;
this.namear = names;
this.msgar = msg;
this.idar = id;
this.profimage=proimg;
this.postimage=postpic;
this.bitdata=bit;
this.commentsnum=comment;
this.objectid=objid;
this.urlString=web;
this.imageLoader = new ImageLoader(context);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View rowView=convertView ;
LayoutInflater inflator = getLayoutInflater();
rowView = inflator.inflate(R.layout.walldata, null);
TextView name1 = (TextView) rowView.findViewById(R.id.name);
TextView message1 = (TextView) rowView.findViewById(R.id.msg);
ImageView profimg= (ImageView) rowView.findViewById(R.id.profile_pic);
ImageView postimg= (ImageView) rowView.findViewById(R.id.picpost);
TextView comments = (TextView) rowView.findViewById(R.id.comment);
Log.e("user",idar.get(position));
Log.e("adapter","adap");
name1.setText(namear.get(position));
if(msgar.get(position)!=""){
message1.setText(msgar.get(position));
}
else
{
message1.setVisibility(View.GONE);
}
if(!postimage.get(position).equals(""))
{try{
imageLoader.DisplayImage(postimage.get(position).replace(" ", "%20"), postimg) ;
}
catch(Exception e){
e.printStackTrace();
}
}
else
{
postimg.setVisibility(View.GONE);
}
try{
imageLoader.DisplayImage(profimage.get(position).replace(" ", "%20"), profimg) ;
}
catch(Exception e){
e.printStackTrace();
}
comments.setText(commentsnum.get(position)+"\t"+"Comments");
comments.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent myintent=new Intent(Wall.this,Comments.class);
myintent.putExtra("name", namear.get(position));
myintent.putExtra("profimg", profimage.get(position));
myintent.putExtra("message", msgar.get(position));
myintent.putExtra("postpic", postimage.get(position));
myintent.putExtra("objectid", objectid.get(position));
myintent.putExtra("commentsnum",commentsnum.get(position));
myintent.putExtra("webservice", urlString.get(position));
startActivity(myintent);
}
});
return rowView;
}
Here is my Imageloader class
public class ImageLoader {
MemoryCache memoryCache=new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
public ImageLoader(Context context){
photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
fileCache=new FileCache(context);
}
public void DisplayImage(String url,ImageView imageView)
{
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url);
if(bitmap!=null)
{
imageView.setBackgroundDrawable(new BitmapDrawable(bitmap));
System.gc();
}
else
{
queuePhoto(url, imageView);
}
}
private Bitmap createScaledBitmap(Bitmap bitmap, int i, int j, int k) {
return null;
}
private void queuePhoto(String url, 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();
}
public Bitmap getBitmap(String url)
{
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(30000);
conn.setReadTimeout(30000);
InputStream is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex){
ex.printStackTrace();
return null;
}
}//Lalit
//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=Wall.width;
final int REQUIRED_SIZE1=Wall.height;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if((width_tmp/2<REQUIRED_SIZE)&&(height_tmp/2<REQUIRED_SIZE1))
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
o2.inSampleSize=scale;
o2.inJustDecodeBounds = false;
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();
}
Bitmap bmp=getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
String tag=imageViews.get(photoToLoad.imageView);
if(tag!=null && tag.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.setBackgroundDrawable(new BitmapDrawable(bitmap));
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels,int width,int height) {
Bitmap output = Bitmap.createBitmap(width,height, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
final float roundPx = pixels;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
I am scaling the images upto my device screen size i.e 480*854,you can find this in imageloader class,I also mention here for easy understanding
final int REQUIRED_SIZE=Wall.width;
final int REQUIRED_SIZE1=Wall.height;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if((width_tmp/2<REQUIRED_SIZE)&&(height_tmp/2<REQUIRED_SIZE1))
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
}
By looking at the error I can understand that memory is not sufficient for loading bitmap,I dont know how can I deallocate memory at runtime.
please help me I referred to many of the links in stackoverflow and others but couldnt fix this issue.
Well, one way around your issue that I can think of would be to use an onScrollListener, and add a flag to your adapter that can be set by the listener.
When the list is scrolling (flag = true) have the adapter skip loading the images. When the list has stopped scrolling (flag = false) have the adapter load the images. You trigger the image load by calling notifyDataSetChanged() on your adapter.
If we call the flag in the adapter isScrolling, your listener might look like this:
listView.setOnScrollListener(new OnScrollListener() {
public void onScroll(AbsListView view, int firstVisible,
int visibleCount, int total) {
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState != 0)
listView.getAdapter()).isScrolling = true;
else {
listView.getAdapter().isScrolling = false;
listView.getAdapter().notifyDataSetChanged();
}
}
});
And in your adapter simply surround your image loading code with this:
if (!isScrolling) {
// code to load images
}
there maybe two causes with your problems.first, you dont reuse the istance of the listview items that had been created. i see your code ,why you note the codes. when you scroll the listview too fast the vm will not able to create instant quick enough ,so it out of memory.and , you have better to use the buffer.
the second, your bitmap is too large, when you scroll too fast ,the vm cant stand for the pressure.to resolve these ,you can reduce the size of the bitmap.
hope that my answear can do something for you.
I am not pretty sure but add these lines after initializing image component
BitmapDrawable bd = (BitmapDrawable) imgView.getDrawable();
if (bd != null)
{
bd.getBitmap().recycle();
imgView.setImageBitmap(null);
}
I want to simply resize my bitmap which i am getting from the following class named Image Loader (Using Lazy Loadng scheme). I want to simply cut short my image size to 60*60 size. But when i use -
bitmap = Bitmap.createScaledBitmap(bitmap, 20, 25, false);
imageView.setImageBitmap(bitmap);
It does the same as I want but the bitmap that comes out is very very blurred. Is there any other way to implement the same.
Here is my Image Loader class in BITMAP dISPLAYER(iNNER cLASS) Where i am setting the image by the code I have shown above.
package com.irant.LazyLoading;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import com.irant.a1techno.R;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;
import android.widget.ImageView;
public class ImageLoader {
MemoryCache memoryCache=new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews = new ConcurrentHashMap<ImageView, String>();
public ImageLoader(Context context){
//Make the background thead low priority. This way it will not affect the UI performance
photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
fileCache=new FileCache(context);
}
final int stub_id=R.drawable.ic_launcher;
public void displayImage(String url, Activity activity, ImageView imageView)
{
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url);
if(bitmap!=null)
{
//bitmap = Bitmap.createScaledBitmap(bitmap, 25, 25, true);
imageView.setImageBitmap(bitmap);
}
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)
{
File f=fileCache.getFile(url);
InputStream is;
//from SD cache
Bitmap bitmap=null;
Bitmap b = decodeFile(f);
if(b!=null)
return b;
//from web
try {
System.out.println("---------------------IMAGE URL: "+url);
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
is=conn.getInputStream();
OutputStream os = new FileOutputStream(f);
Utils.CopyStream(is, os);
os.close();
bitmap = ShrinkBitmap(url, 60, 60);
bitmap = decodeFile(f);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 40, baos);
return bitmap;
} catch (Exception ex){
System.out.println("---------------------IMAGE URL: "+url);
ex.printStackTrace();
//stopThread();
return null;
}
finally{
bitmap = null;
is=null;
}
}
Bitmap ShrinkBitmap(String file, int width, int height){
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);
int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)height);
int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)width);
if (heightRatio > 1 || widthRatio > 1)
{
if (heightRatio > widthRatio)
{
bmpFactoryOptions.inSampleSize = heightRatio;
} else {
bmpFactoryOptions.inSampleSize = widthRatio;
}
}
bmpFactoryOptions.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);
return bitmap;
}
//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=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;
}
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
}
catch(OutOfMemoryError e)
{
e.printStackTrace();
}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;
}
}
}
public 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);
memoryCache.put(photoToLoad.url, bmp);
String tag=imageViews.get(photoToLoad.imageView);
if(tag!=null && tag.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
System.out.println("This is the Exit Point............");
}
}
}
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)
{
bitmap = Bitmap.createScaledBitmap(bitmap, 20, 25, false);
imageView.setImageBitmap(bitmap);
}
else
imageView.setImageResource(stub_id);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
}
Please reply me.
Thanks in advance
Please dont down the vote if you don't understand. Just help me if anybody can.
Thanks
The default scaleType of an ImageView is FIT_CENTER, meaning that whatever Image you give to the ImageView will be scaled so that one of the axis will be (in your case) 110dp. So passing it a bitmap that has been scaled down to 10*10 will stretch that bitmap to fill the 110*110 space, and a 10*10 stretched that big will be very blurry indeed. Try using one of the other scaleTypes, specifically CENTER to see the unblurred, normal sized version of the image:
imageView.setScaleType(ImageView.ScaleType.CENTER);
If this isn't the effect you want, know that no matter what you do, shrinking an image and then stretching it back out again will always make it a lower quality than what you started with. A lot of what you seem to be trying to do in the code sample above has been done for you by the ImageView class itself - I would encourage you to take a closer look at the ScaleTypes and what they can do for you
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.