AsyncTask and Out Of Memory issue in Android - android

According to my application first I copy all the images from my resources to the internal memory and then on Image Slide to left or right I get the image from memory with its index and show it there. And I'm doing it with AsynTask. And after around 10 images are shown application goes to black screen and log cat says "external allocation too large for this process." According to the things I read here I think the problem is about AsyncTask, I cannot free the memory which has been used for these tasks.
I have three different Activities which are used to show images as a gallery, and each of these activities are using asyncTask to show the images.
Here is some of my code below, and any help will be appreciatead, Thanks in advance.
Here is my Activity used to execute image downloader according to sliding images.
lid1 = new LocalImageDownloader(imageSwitcher, myContext, path, nameList.get(curIndex) );
lid1.execute();
imageSwitcher.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
downX = (int) event.getX();
Log.i("event.getX()", " downX " + downX);
return true;
}
else if (event.getAction() == MotionEvent.ACTION_UP) {
upX = (int) event.getX();
Log.i("event.getX()", " upX " + downX);
if (upX - downX > 100) {
//curIndex current image index in array viewed by user
curIndex--;
if (curIndex < 0) {
curIndex = imageList.size()-1;
}
imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_in_left));
imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_out_right));
lid1.cancel(true);
lid1 = new LocalImageDownloader(imageSwitcher, myContext, path, nameList.get(curIndex) );
lid1.execute();
}
else if (downX - upX > -100) {
curIndex++;
if (curIndex == imageList.size() ) {
curIndex = 0;
}
imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_in_right));
imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_out_left));
lid1.cancel(true);
lid1 = new LocalImageDownloader(imageSwitcher, myContext, path, nameList.get(curIndex) );
lid1.execute();
}
return true;
}
return false;
}
});
and this is my AsyncTask to get images from internal memory,
public class LocalImageDownloader extends AsyncTask<String, Void, Bitmap> {
String url;
Drawable d;
Context myContext;
String path;
String fileName;
ProgressDialog dialog;
int REQUIRED_SIZE=600;
private final WeakReference<ImageSwitcher> imageViewReference;
public LocalImageDownloader(ImageSwitcher imageSwitcher,Context myContext, String path, String fileName) {
this.myContext = myContext;
this.path = path;
this.fileName = fileName;
imageViewReference = new WeakReference<ImageSwitcher>(imageSwitcher);
}
#Override
protected Bitmap doInBackground(String... urls) {
publishProgress();
return null;
}
#Override
protected void onPreExecute() {
dialog = ProgressDialog.show(myContext, "", "Loading Images...", true);
super.onPreExecute();
}
#Override
protected void onPostExecute(Bitmap result) {
try {
if (imageViewReference != null) {
ImageSwitcher imageSwitcher = imageViewReference.get();
if (imageSwitcher != null) {
imageSwitcher.setImageDrawable(getLocalImage());
}
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
dialog.dismiss();
}
public Drawable getLocalImage() throws IOException {
File file = new File(path,fileName);
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(file),null,o);
//The new size we want to scale to
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=this.REQUIRED_SIZE && o.outHeight/scale/2>=this.REQUIRED_SIZE)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
o.inJustDecodeBounds = false;
return new BitmapDrawable(BitmapFactory.decodeStream(new FileInputStream(file), null, o2));
}
}
EDIT:
I have applied some of the ways to use bitmaps more efficiently and now I'm pushing them to the memory but I still have almost the same error. After some of the images are stored in memory, for some of the images I get black screen and having the same error."external allocation too large for this process." Any idea how to do it ?
Here is the memory cache code below, and I'm sending my MemoryCache object to AsyncTask as a parameter.
public class MemoryCache {
private static final String TAG = "MemoryCache";
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 MemoryCache(){
//use 50% of available heap size
setLimit(Runtime.getRuntime().maxMemory()/2);
}
public void setLimit(long new_limit){
limit=new_limit;
Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
}
public Bitmap get(String id){
try{
if(!cache.containsKey(id))
return null;
//NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78
return cache.get(id);
}catch(NullPointerException ex){
return null;
}
}
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() {
Log.i(TAG, "cache size="+size+" length="+cache.size());
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;
}
Log.i(TAG, "Clean cache. New size "+cache.size());
}
}
public void clear() {
cache.clear();
}
long getSizeInBytes(Bitmap bitmap) {
if(bitmap==null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
public boolean contains(String key) {
if(cache.containsKey(key)) {
return true;
}
return false;
}
}

Try this link for managing your bitmap cache, there are many solutions for preventing the outofMemory exception. http://developer.android.com/training/displaying-bitmaps/index.html

It has nothing to do with AsyncTask. You will have to use Bitmaps more efficiently.
Search for Bitmap OOM;
do some sampling of your downloaded images.as in here
Also search for Android SoftReference.

I have found a way and now it works quite well, instead of getting images with file streams and bitmaps now I do it by setting the drawable with the code below.As for the images downloaded from internet , anybody who wants to do something like that, just simply download images and save it either in internal memory or in external memory then just give the path to the image.
ImageView imageView = (ImageView) findViewById(R.id.image);
File filePath = getFileStreamPath(fileName);
imageView.setImageDrawable(Drawable.createFromPath(filePath));

Related

How to apply bitmap recycle in recycleView because of out of memory exception

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);
}

How to reload images, if any exception ocurrs using imageGetter

I'm trying to parse images that comes in <img> tag using ImageGetter. I'm successfully able to parse and download the images. But,in worst cases, like no coonectivity, low network, if the image is not loaded, I want to reload the image. As per my understanding , I reload the image in onPostExecute() method of Asynctask when bitmap is null. But for this, Again I have to call the Asynctask method. Is there any other alternative to reload the image.
Below is my Imagegetter Code:
public class UrlImageParser implements Html.ImageGetter {
private static String TAG = "ImageParser";
private TextView mContainer;
private Context mContext;
Point outSize=new Point();
float destWidth=1;
float destHeight=1;
public UrlImageParser(TextView t, Context context) {
mContainer = t;
mContext = context;
}
#Override
public Drawable getDrawable(String source) {
LevelListDrawable d = new LevelListDrawable();
Drawable empty = mContext.getResources().getDrawable(R.drawable.story_img_placeholder);
d.addLevel(0, 0, empty);
d.setBounds(0, 0, empty.getIntrinsicWidth(), empty.getIntrinsicHeight());
new LoadImage().execute(source, d);
return d;
}
class LoadImage extends AsyncTask<Object, Void, Bitmap> {
private LevelListDrawable mDrawable;
#Override
protected Bitmap doInBackground(Object... params) {
String source = (String) params[0];
mDrawable = (LevelListDrawable) params[1];
Log.d(TAG, "doInBackground " + source);
try {
InputStream is = new URL(source).openStream();
return BitmapFactory.decodeStream(is);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
Log.d(TAG, "onPostExecute drawable " + mDrawable);
Log.d(TAG, "onPostExecute bitmap " + bitmap);
if (bitmap != null) {
BitmapDrawable d = new BitmapDrawable(scaleBitmap(bitmap));
mDrawable.addLevel(1, 1, d);
/*int width = bitmap.getWidth();
int screenWidth = Utility.getScreenWidth(mContext);
int height = (int) (screenWidth * 0.62);*/
mDrawable.setBounds(0, 0, d.getBitmap().getWidth(), d.getBitmap().getHeight());
mDrawable.setLevel(1);
/*// redraw the image by invalidating the container
UrlImageParser.this.container.invalidate(); */
if (mContainer != null) {
mContainer.setText(mContainer.getText());
}
}else{
//Some error occured, send the request again
}
}
}
private Bitmap scaleBitmap(Bitmap mFile){
Display display = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if (android.os.Build.VERSION.SDK_INT >= 13){
display.getSize(outSize);
destWidth = outSize.x;
destHeight = outSize.y;
}else{
destWidth=display.getWidth();
destHeight=display.getHeight();
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap orig = mFile;
float srcWidth = orig.getWidth();
float srcHeight = orig.getHeight();
Bitmap resized=Bitmap.createScaledBitmap(orig, (int)Math.round(destWidth), (int)Math.round(destWidth * srcHeight /srcWidth), true);
destWidth=1;
destHeight=1;
return resized;
}
}
The best way to do this is to never leave doInBackground() or to start the entire AsyncTask again. You could do something like this:
int failureCounter = 0;
#Override
protected Bitmap doInBackground(Object... params) {
try {
String source = (String) params[0];
mDrawable = (LevelListDrawable) params[1];
Log.d(TAG, "doInBackground " + source);
InputStream is = new URL(source).openStream();
return BitmapFactory.decodeStream(is);
} catch (Exception e) {
e.printStackTrace();
if(this.failureCounter++ >= 5) {
return null;
} else {
return this.doInBackground(params);
}
}
}
This code snippet will retry to load the image 5 times before it returns null. You should limit the number of tries to prevent a StackOverflowError and to limit the time your task is running as AsyncTasks are not ment to be a long-running background task.

How To Set in Background Of Image View

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

app is slow with images in android

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

How to use cache memory for image download?

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

Categories

Resources