Saturday, 27 April 2013

Load large images from server using image Loader class in android

Load large images from server using image Loader class in android

There is major problem of "Bitmap out of memory"  while load large images from server. here there is Image loader class for load large image efficiently. i have set tag in loader class for image display like

                                pass int value (1,2,3)
                                1 = thumbImage (ScaleType.FIT_XY)
                                2 = fullImage (ScaleType.FIT_XY)
                                3 = fullImage (ScaleType.FIT_START)





Main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <ImageView
        android:id="@+id/imgPhoto"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"       
        android:contentDescription="@string/app_name"
        android:scaleType="center"
        >
    </ImageView>

</RelativeLayout>

animation_loding.xml: This is the animation while loading images from server. Put couple images in drawable.

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false" >

    <item
        android:drawable="@drawable/new1"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new2"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new3"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new4"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new5"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new6"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new7"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new8"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new9"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new10"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new11"
        android:duration="50"/>
    <item
        android:drawable="@drawable/new12"
        android:duration="50"/>

</animation-list>


Image Loader Class:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;

public class ImageLoader
{
       MemoryCache memoryCache = new MemoryCache();
       FileCache fileCache;
       private Map<ImageView, String> imageViews = Collections
       .synchronizedMap(new WeakHashMap<ImageView, String>());
       ExecutorService executorService;
       Context mContext;

       Bitmap bitmap;
       AnimationDrawable frameAnimation;
       private int i_size = 320;
       public ImageLoader(Context context)
       {
              fileCache = new FileCache(context);
              mContext = context;
              executorService = Executors.newFixedThreadPool(5);
       }

       private int mTag = 0;

       /**
        * LoadImage method for load image from web
        *@param url
        *@param imageView
        *@param tag
        *                         pass int value (1,2,3) for<br/>
        *                         1 = thumbImage (ScaleType.FIT_XY),<br/>
        *                         2 = fullImage (ScaleType.FIT_XY),<br/>
        *                         3 = fullImage (ScaleType.FIT_START)
        */
       public synchronized void DisplayImage(String url, ImageView imageView, int tag)
       {

              this.mTag = tag;
              if(mTag == 1 || mTag == 4)
              {
                     i_size =Constant.MAX_ThumbImage_SIZE;
              }
              else
              {
                     i_size =Constant.MAX_Image_SIZE;
              }
              imageViews.put(imageView, url);
              bitmap = memoryCache.get(url);
              if (bitmap != null)
              {                   
                     Log.i("memoryCache""bitmap");
                     imageView.setImageBitmap(bitmap);
              }
              else
              {
                     Log.i("memoryCache_touch""bitmap null");
                     queuePhoto(url, imageView);

                     frameAnimation = (AnimationDrawable)mContext.getResources().getDrawable(R.anim.animation_loding);
                     imageView.setScaleType(ScaleType.CENTER);
                     imageView.setImageDrawable(frameAnimation);

                     Timer timer = new Timer();
                     timer.schedule(new TimerTask()
                     {
                           @Override
                           public void run()
                           {
                                  frameAnimation.start();
                           }
                     }, 0);
              }
       }

       private void queuePhoto(String url, ImageView imageView)
       {
              PhotoToLoad p = new PhotoToLoad(url, imageView);
              //executor.execute(new PhotosLoader(p));
              executorService.submit(new PhotosLoader(p));
       }

       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);
                     conn.setInstanceFollowRedirects(true);
                     InputStream is = conn.getInputStream();
                     OutputStream os = new FileOutputStream(f);
                     Utils.CopyStream(is, os);
                     os.close();
                     bitmap = decodeFile(f);
                     return bitmap;
              }
              catch (Throwable ex)
              {
                     ex.printStackTrace();
                     if(ex instanceof OutOfMemoryError)
                           memoryCache.clear();
                     return null;
              }

       }

       // decodes image and scales it to reduce memory consumption
       public Bitmap decodeFile(File f)
       {
              try
              {
                     BitmapFactory.Options o = new BitmapFactory.Options();
                     o.inJustDecodeBounds = true;
                     FileInputStream stream1=new FileInputStream(f);
                     BitmapFactory.decodeStream(stream1,null,o);
                     stream1.close();

                     //Find the correct scale value. It should be the power of 2.
                     final int REQUIRED_SIZE=i_size;
                     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;
                     FileInputStream stream2=new FileInputStream(f);
                     Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2);
                     stream2.close();
                     return bitmap;
              }catch (Throwable e) {
                     // TODO: handle exception
                     e.printStackTrace();
                     if(e instanceof OutOfMemoryError)
                           memoryCache.clear();
                     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);
                     if(bmp != null)
                     {
                           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.setBackgroundResource(0);
                           photoToLoad.imageView.setImageBitmap(bitmap);
                           if(mTag==1 || mTag == 2)
                           {
                                  photoToLoad.imageView.setScaleType(ScaleType.FIT_XY); 
                           }
                           else if (mTag == 4)
                           {
                                  photoToLoad.imageView.setScaleType(ScaleType.FIT_CENTER);
                           }
                           else if (mTag == 5)
                           {
                                  photoToLoad.imageView.setScaleType(ScaleType.CENTER_CROP);   
                           }
                           else
                           {
                                  photoToLoad.imageView.setScaleType(ScaleType.FIT_START);
                           }     
                     }
                     else
                     {
                           Drawable drawable = mContext.getResources().getDrawable(R.drawable.no_image);
                           photoToLoad.imageView.setImageDrawable(drawable);
                     }
              }
       }

       public void clearCache()
       {
              memoryCache.clear();
              fileCache.clear();
       }

}


FileCache:
import java.io.File;
import android.content.Context;

public class FileCache {
   
    private File cacheDir;
   
    public FileCache(Context context){
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
            cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"ImageLoader");
        else
            cacheDir=context.getCacheDir();
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }
   
    public File getFile(String url){
        String filename=String.valueOf(url.hashCode());
        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: 
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import android.graphics.Bitmap;

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:  
import java.io.InputStream;
import java.io.OutputStream;

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){}
    }
}
Constant:  
public class Constant
{
       public static int MAX_Image_SIZE = 320;
       public static int MAX_ThumbImage_SIZE = 125;
}

MainActivity:
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.widget.ImageView;

public class MainActivity extends Activity
{

       ImageLoader img_loader;
       ImageView imgPhoto=null;

       @Override
       protected void onCreate(Bundle savedInstanceState)
       {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.main);

              img_loader =new ImageLoader(MainActivity.this);       
              imgPhoto = (ImageView)findViewById(R.id.imgPhoto);           
              getImagesSize();
              img_loader.DisplayImage("http://talkingpointsmemo.com/images/google-android-mascot.jpg",imgPhoto,4);
       }

       private void getImagesSize()
       {            
              DisplayMetrics dm = new DisplayMetrics();
              getWindowManager().getDefaultDisplay().getMetrics(dm);
              int w = dm.widthPixels;
              int h = dm.heightPixels;

              if(w <= 240 && h <= 320)
              {
                     Constant.MAX_ThumbImage_SIZE = 100;
                     Constant.MAX_Image_SIZE = 400;

              }
              else if(w <= 320 && h <= 480)
              {
                     Constant.MAX_ThumbImage_SIZE = 100;
                     Constant.MAX_Image_SIZE = 600;

              }
              else if(w <= 480 && h <= 854)
              {
                     Constant.MAX_ThumbImage_SIZE = 150;
                     Constant.MAX_Image_SIZE = 900;

              }
              else
              {
                     Constant.MAX_ThumbImage_SIZE = 200;
                     Constant.MAX_Image_SIZE = 1000;

              }
       }
} 
Permission:
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


Download Sample: