Monday, June 27, 2011

Android lazy image loader example

Android lazy image loader example


Suppose ,you have a list View in your activity where you have to download the data and corresponding images and show it in the list.

Generally if you download both the data and images once and after  downloading of data you will show it in the list which will take a longer duration particularly depends on net connectivity speed.

For Reducing the time ,first we download the text ie.,names,links except images .we will put the download links in a stack and we will download them after displaying the list-view by using late downloading of images.
First the list will show and images will show in their corresponding position in the list whenever it got downloaded exactly like android market application.
This will reduce the time and we can download as many items we want to show as like working of android market list view.






 ImageLoader.java


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.URL;
import java.util.HashMap;
import java.util.Stack;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;

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;
   
    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);
       
        //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();
    }
   
    final int stub_id=R.drawable.stub;
    public void DisplayImage(String url, Activity activity, ImageView imageView)
    {
        if(cache.containsKey(url))
            imageView.setImageBitmap(cache.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();
            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++;
            }
           
            //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();
                        }
                        Bitmap bmp=getBitmap(photoToLoad.url);
                        cache.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 void clearCache() {
        //clear memory cache
        cache.clear();
       
        //clear SD cache
        File[] files=cacheDir.listFiles();
        for(File f:files)
            f.delete();
    }

}

15 comments:

  1. sorry but what is int stub???? Tnks

    ReplyDelete
  2. STUB_ID is the resource id passed in for the default image to display while the real image downloads.

    While I'm on here, thanks a lot for this vijay. It helped me a lot as a starter.

    Have you thought at all about adding the functionality to handle having a max number of bitmap's loaded in to memory at one time? I've tried and I'm running in to memory leak problems. If any ImageView in any activity has a reference to a bitmap in the queue it can't be garbage collected. So there needs to be a way to manage all ImageViews being used in your app (in adapters or single instances). You need to easily clear all references to a bitmap in one call.

    ReplyDelete
    Replies
    1. Hi aden,
      hmm thank u. suppose the real images not there mean that time also it will display. i'm not getting any memory leakage?

      Delete
  3. thank u for shear you knowledge

    ReplyDelete
  4. Hi,

    it works fine in simulator. But when I tried on my phone (Galaxy S2), the images are not loading... please advise

    ReplyDelete
  5. I hope Android 5 coming soon
    Also visit my blog post :: Android3Tablet

    ReplyDelete
  6. I'm Doing a project in which i need to Convert Speech to text. There are so many codings with Google voice...But there are no codings for without Internet except pocketsphinx... Pocketsphinx doesn't work 4 me ... Hope u send the source code to

    l.rakesh678@gmail.com

    ReplyDelete
  7. I am sure this article has touched all the internet visitors, its
    really really nice paragraph on building up new website. Check out my website to get more info about work from home,
    if you like.
    Also visit my weblog making money

    ReplyDelete
  8. Hi thеrе just ωanted to give you а quiсk heаԁs up аnԁ lеt уou know a fеω of the pictures
    аren't loading correctly. I'm nοt
    sure why but Ӏ think itѕ a linking іsѕue.
    I've tried it in two different internet browsers and both show the same results.
    Also visit my web-site :: teknaf

    ReplyDelete
  9. You example is nice. But it's a lengthy tutorial. After searching the internet I found another one.
    http://www.coderzheaven.com/2012/09/23/simplest-lazy-loading-listview-android-data-populated-mysql-database-php/

    ReplyDelete
  10. The (Activity) activity variable for queuePhoto and DisplayImage is never used. Why do you include it?

    ReplyDelete

Check out this may be help you

Related Posts Plugin for WordPress, Blogger...