当前位置: 代码迷 >> 综合 >> Android中的延迟加载系列4(ImageView)
  详细解决方案

Android中的延迟加载系列4(ImageView)

热度:11   发布时间:2024-01-11 12:29:50.0
在Android应用程序的开发中,从网络或者服务器上取得图片,往往需要花费一定的时间,占用一定的用户带宽,当页面有大量的图片时,如果不采取延迟加载的方法,则客户端需要等到所有的图片都获取之后,才可以呈现完整界面,这就可能导致界面反应不流畅,影响用户体验。


图片延迟加载的原理其实非常简单,有两种思路:

第一种思路是后台启动Thread下载图片,下载完成后,通过Message Handle的方式把控制权转让给UI Thread并绘制图片。此方法的优点是比较简单,缺点是不容易封装。

第二种思路是启动异步任务AsyncTask,通过doInBackground()方法获得图片资源之后,再通过onPostExecute()方法在UI Thread中绘制取好的图片。


以上两种方式都可以很好地处理图片的延迟加载。本文通过第一种方式来处理,对图片的延迟加载进行封装,并对性能进行如下优化:

1) 图片加载的线程优先级比UI低一级,这样可以优先处理UI任务。

2) 在内存、磁盘缓存下载的应用程序图片。读取图片的顺序为 内存 -> 磁盘 -> 网络,这样可以避免下载重复的图片,节省网络流量,并且提高响应速度和性能。

3) 本地缓存的图片可能由于某种原因过期,而与服务端不一致,比如服务端更新了图片资源,这个时候本地并不知道,从而导致了图片的不一致性,这也是采取缓存技术提高性能而导致的副作用。为了解决这个问题,可以引入时间戳的概念,当时间戳发生变化之后,重新从网络上获取图片并缓存。


以下将做简要的说明。


首先建立LazyImage对象,此对象包括了图片image,图片的网络资源链接url,以及图片所对应的时间戳(从服务端获得,如果没有时间戳的固定图片,也可以不用设置)。

[java] view plain copy print ?
  1. package com.whyonly.core.bean;  
  2.   
  3. import android.graphics.Bitmap;  
  4.   
  5. public abstract class LazyImage {  
  6.       
  7.     private Bitmap image;  
  8.     private String image_url;  
  9.     private long timestamp;  
  10.       
  11.       
  12.     public boolean hasLoadPhoto(){  
  13.         return image==null ? false : true;  
  14.     }  
  15.     public long getTimestamp() {  
  16.         return timestamp;  
  17.     }  
  18.     public void setTimestamp(long timestamp) {  
  19.         this.timestamp = timestamp;  
  20.         this.hashCode();  
  21.     }  
  22.       
  23.       
  24.       
  25.     public Bitmap getImage() {  
  26.         return image;  
  27.     }  
  28.     public void setImage(Bitmap image) {  
  29.         this.image = image;  
  30.     }  
  31.     public String getImage_url() {  
  32.         return image_url;  
  33.     }  
  34.     public void setImage_url(String image_url) {  
  35.         this.image_url = image_url;  
  36.     }  
  37.   
  38.       
  39.     public String toFileName(){ //convert the url + timestamp to file name to instore to local disk   
  40.         String fileName = "";  
  41.         if(image_url!=null && image_url.indexOf("/")!=-1 ){  
  42.             fileName=image_url.substring(image_url.lastIndexOf("/")+1,image_url.length());  
  43.         }  
  44.         return  fileName+"_"+timestamp;  
  45.     }  
  46.       
  47.       
  48.   
  49. }  
package com.whyonly.core.bean;
import android.graphics.Bitmap;
public abstract class LazyImage {
private Bitmap image;
private String image_url;
private long timestamp;
public boolean hasLoadPhoto(){
return image==null ? false : true;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
this.hashCode();
}
public Bitmap getImage() {
return image;
}
public void setImage(Bitmap image) {
this.image = image;
}
public String getImage_url() {
return image_url;
}
public void setImage_url(String image_url) {
this.image_url = image_url;
}
public String toFileName(){ //convert the url + timestamp to file name to instore to local disk
String fileName = "";
if(image_url!=null && image_url.indexOf("/")!=-1 ){
fileName=image_url.substring(image_url.lastIndexOf("/")+1,image_url.length());
}
return  fileName+"_"+timestamp;
}
}

建立一个下载任务

[java] view plain copy print ?
  1. //Task for the queue   
  2.     private class PhotoToLoad  
  3.     {  
  4.         public LazyImage lazyImage;  
  5.         public ImageView imageView;  
  6.         public boolean saveDisk;  
  7.         public PhotoToLoad(LazyImage l, ImageView i, boolean s){  
  8.             lazyImage=l;   
  9.             imageView=i;  
  10.             saveDisk = s;  
  11.         }  
  12.     }  
//Task for the queue
private class PhotoToLoad
{
public LazyImage lazyImage;
public ImageView imageView;
public boolean saveDisk;
public PhotoToLoad(LazyImage l, ImageView i, boolean s){
lazyImage=l; 
imageView=i;
saveDisk = s;
}
}

以及下载队列

[java] view plain copy print ?
  1. //stores list of photos to download   
  2. class PhotosQueue  
  3. {  
  4.     private Stack<PhotoToLoad> photosToLoadStack=new Stack<PhotoToLoad>();  
  5.       
  6.     //removes all instances of this ImageView   
  7.     public void clean(ImageView image)  
  8.     {  
  9.         for(int j=0 ;j<photosToLoadStack.size();){  
  10.             if(photosToLoadStack.get(j).imageView==image)  
  11.                 photosToLoadStack.remove(j);  
  12.             else  
  13.                 ++j;  
  14.         }  
  15.     }  
  16. }  
    //stores list of photos to download
class PhotosQueue
{
private Stack<PhotoToLoad> photosToLoadStack=new Stack<PhotoToLoad>();
//removes all instances of this ImageView
public void clean(ImageView image)
{
for(int j=0 ;j<photosToLoadStack.size();){
if(photosToLoadStack.get(j).imageView==image)
photosToLoadStack.remove(j);
else
++j;
}
}
}




接着建立内存缓冲类:

[java] view plain copy print ?
  1. class MemoryCache {  
  2.     private HashMap<String, SoftReference<Bitmap>> cache=new HashMap<String, SoftReference<Bitmap>>();  
  3.       
  4.     public Bitmap get(String id){  
  5.         if(!cache.containsKey(id))  
  6.             return null;  
  7.         SoftReference<Bitmap> ref=cache.get(id);  
  8.         return ref.get();  
  9.     }  
  10.       
  11.     public void put(String id, Bitmap bitmap){  
  12.         cache.put(id, new SoftReference<Bitmap>(bitmap));  
  13.     }  
  14.   
  15.     public void clear() {  
  16.         cache.clear();  
  17.     }  
  18. }  
class MemoryCache {
private HashMap<String, SoftReference<Bitmap>> cache=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();
}
}


和本地磁盘缓冲类

[java] view plain copy print ?
  1. class FileCache {  
  2.     
  3.     private File cacheDir;  
  4.       
  5.     public FileCache(Context context){  
  6.         //Find the dir to save cached images   
  7. //        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))   
  8.             cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"whyonly/cache");  
  9. //        else   
  10. //            cacheDir=context.getCacheDir();   
  11.         Log.d("ImageLoader","cacheDir:"+cacheDir);  
  12.         if(!cacheDir.exists())  
  13.             cacheDir.mkdirs();  
  14.     }  
  15.       
  16.     public File getFile(LazyImage lazyImage){     
  17.         String filename= lazyImage.toFileName();  
  18.         File f = new File(cacheDir, filename);  
  19.         return f;  
  20.           
  21.     }  
  22.       
  23.     public void clear(){  
  24.         File[] files=cacheDir.listFiles();  
  25.         for(File f:files)  
  26.             f.delete();  
  27.     }  
  28.   
  29. }  
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(),"whyonly/cache");
//        else
//            cacheDir=context.getCacheDir();
Log.d("ImageLoader","cacheDir:"+cacheDir);
if(!cacheDir.exists())
cacheDir.mkdirs();
}
public File getFile(LazyImage lazyImage){   
String filename= lazyImage.toFileName();
File f = new File(cacheDir, filename);
return f;
}
public void clear(){
File[] files=cacheDir.listFiles();
for(File f:files)
f.delete();
}
}

把图片资源显示到ImageView

[java] view plain copy print ?
  1. //Used to display bitmap in the UI thread   
  2.     class BitmapDisplayer implements Runnable  
  3.     {  
  4.         Bitmap bitmap;  
  5.         ImageView imageView;  
  6.         public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}  
  7.         public void run()  
  8.         {  
  9.             if(bitmap!=null)  
  10.                 imageView.setImageBitmap(bitmap);  
  11.             else  
  12.                 imageView.setImageResource(defaultImageResId);  
  13.         }  
  14.     }  
//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(defaultImageResId);
}
}

最后是通过线程来控制图片的下载和显示过程

[java] view plain copy print ?
  1. class PhotosLoaderThread extends Thread {  
  2.     public void run() {  
  3.         try {  
  4.             while(true)  
  5.             {  
  6.                 //thread waits until there are any images to load in the queue   
  7.                 if(photosQueue.photosToLoadStack.size()==0)  
  8.                     synchronized(photosQueue.photosToLoadStack){  
  9.                         photosQueue.photosToLoadStack.wait();  
  10.                     }  
  11.                 if(photosQueue.photosToLoadStack.size()!=0)  
  12.                 {  
  13.                     PhotoToLoad photoToLoad;  
  14.                     synchronized(photosQueue.photosToLoadStack){  
  15.                         photoToLoad=photosQueue.photosToLoadStack.pop();  
  16.                     }  
  17.                     Bitmap bmp=getBitmap(photoToLoad.lazyImage,photoToLoad.saveDisk);  
  18.                     memoryCache.put(photoToLoad.lazyImage.toFileName(), bmp);  
  19.                     String tag=imageViews.get(photoToLoad.imageView);  
  20.                     if(tag!=null && tag.equals(photoToLoad.lazyImage.toFileName())){  
  21.                         BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);  
  22.                         Activity a=(Activity)photoToLoad.imageView.getContext();  
  23.                         a.runOnUiThread(bd);  
  24.                     }  
  25.                 }  
  26.                 if(Thread.interrupted())  
  27.                     break;  
  28.             }  
  29.         } catch (InterruptedException e) {  
  30.             //allow thread to exit   
  31.         }  
  32.     }  
  33. }  
    class PhotosLoaderThread extends Thread {
public void run() {
try {
while(true)
{
//thread waits until there are any images to load in the queue
if(photosQueue.photosToLoadStack.size()==0)
synchronized(photosQueue.photosToLoadStack){
photosQueue.photosToLoadStack.wait();
}
if(photosQueue.photosToLoadStack.size()!=0)
{
PhotoToLoad photoToLoad;
synchronized(photosQueue.photosToLoadStack){
photoToLoad=photosQueue.photosToLoadStack.pop();
}
Bitmap bmp=getBitmap(photoToLoad.lazyImage,photoToLoad.saveDisk);
memoryCache.put(photoToLoad.lazyImage.toFileName(), bmp);
String tag=imageViews.get(photoToLoad.imageView);
if(tag!=null && tag.equals(photoToLoad.lazyImage.toFileName())){
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
}
}
}

完整的图片加载类如下:

[java] view plain copy print ?
  1. package com.whyonly.core.wrapper;  
  2.   
  3. import java.io.File;  
  4. import java.io.InputStream;  
  5. import java.lang.ref.SoftReference;  
  6. import java.net.HttpURLConnection;  
  7. import java.net.URL;  
  8. import java.util.Collections;  
  9. import java.util.HashMap;  
  10. import java.util.Map;  
  11. import java.util.Stack;  
  12. import java.util.WeakHashMap;  
  13.   
  14. import com.whyonly.core.bean.LazyImage;  
  15. import com.whyonly.core.util.ImageUtil;  
  16.   
  17. import android.app.Activity;  
  18. import android.content.Context;  
  19. import android.graphics.Bitmap;  
  20. import android.util.Log;  
  21. import android.widget.ImageView;  
  22.   
  23. public class ImageLoader {  
  24.       
  25.     private static final String TAG = "ImageLoader";  
  26.     private MemoryCache memoryCache;  
  27.     private FileCache fileCache;  
  28.     private Map<ImageView, String> imageViews;  
  29.       
  30.     private PhotosLoaderThread photoLoaderThread;  
  31.     private PhotosQueue photosQueue;  
  32.     private int defaultImageResId;  
  33.     //private Context context;   
  34.       
  35.     public ImageLoader(Context context,int defaultImageResId){  
  36.         //Make the background thead low priority. So it will not affect the UI performance   
  37.         photoLoaderThread=new PhotosLoaderThread();  
  38.         photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);    
  39.         memoryCache=new MemoryCache();  
  40.         fileCache=new FileCache(context);  
  41.         imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());  
  42.         photosQueue=new PhotosQueue();  
  43.         //this.context = context;   
  44.         this.defaultImageResId = defaultImageResId;  
  45.     }  
  46.       
  47.     public void displayImage(LazyImage lazyImage, ImageView imageView){  
  48.         displayImage(lazyImage,imageView,true);  
  49.     }  
  50.       
  51.     public void displayImage(LazyImage lazyImage, ImageView imageView,boolean saveDisk)  
  52.     {  
  53.         imageViews.put(imageView, lazyImage.toFileName());    
  54.         if(lazyImage.getImage()!=null){  
  55.             imageView.setImageBitmap(lazyImage.getImage());//get from lazy image   
  56.             //Log.d(TAG,"----LazyImage cache:"+lazyImage.toFileName());   
  57.         }else{  
  58.             Bitmap bitmap=memoryCache.get(lazyImage.toFileName());//get from memory cache   
  59.             if(bitmap!=null){  
  60.                 lazyImage.setImage(bitmap);  
  61.                 imageView.setImageBitmap(bitmap);  
  62.                 //Log.d(TAG,"----MEMORY cache:"+lazyImage.toFileName());   
  63.             }else  
  64.             {  
  65.                 if(defaultImageResId>0)  
  66.                     imageView.setImageResource(defaultImageResId);  
  67.                 else  
  68.                     imageView.setImageBitmap(null);  
  69.                 if(lazyImage.getImage_url() != null)  
  70.                     queuePhoto(lazyImage, imageView,saveDisk);//get from SD card or web    
  71.             }      
  72.         }  
  73.     }  
  74.           
  75.     private void queuePhoto(LazyImage lazyImage, ImageView imageView,boolean saveDisk)  
  76.     {  
  77.         //This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them.    
  78.         photosQueue.clean(imageView);  
  79.         PhotoToLoad photosToLoad=new PhotoToLoad(lazyImage, imageView, saveDisk);  
  80.         synchronized(photosQueue.photosToLoadStack){  
  81.             photosQueue.photosToLoadStack.push(photosToLoad);  
  82.             photosQueue.photosToLoadStack.notifyAll();  
  83.         }  
  84.           
  85.         //start thread if it's not started yet   
  86.         if(photoLoaderThread.getState()==Thread.State.NEW)  
  87.             photoLoaderThread.start();  
  88.     }  
  89.       
  90.     private Bitmap getBitmap(LazyImage lazyImage,boolean saveDisk)   
  91.     {  
  92.         if(!saveDisk){  
  93.             return ImageUtil.returnBitMap(lazyImage.getImage_url());  
  94.         }  
  95.           
  96.         File f=fileCache.getFile(lazyImage);  
  97.         //from SD cache   
  98.         Bitmap b = ImageUtil.file2Bitmap(f);  
  99.         if(b!=null){  
  100.             lazyImage.setImage(b);  
  101.             //Log.d(TAG,"----FILE cache:"+lazyImage.toFileName());   
  102.             return b;  
  103.         }   
  104.           
  105.         //from web   
  106.         try {  
  107.             URL imageUrl = new URL(lazyImage.getImage_url());  
  108.             HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();  
  109.             conn.setConnectTimeout(30000);  
  110.             conn.setReadTimeout(30000);  
  111.             InputStream is=conn.getInputStream();  
  112.             ImageUtil.inputStream2File(is, f);  
  113.             lazyImage.setImage(ImageUtil.file2Bitmap(f));  
  114.             //Log.d(TAG,"----WEB URL:"+lazyImage.toFileName());   
  115.             return lazyImage.getImage();  
  116.         } catch (Exception ex){  
  117.            //ex.printStackTrace();   
  118.            return null;  
  119.         }  
  120.     }  
  121.   
  122.       
  123.     //Task for the queue   
  124.     private class PhotoToLoad  
  125.     {  
  126.         public LazyImage lazyImage;  
  127.         public ImageView imageView;  
  128.         public boolean saveDisk;  
  129.         public PhotoToLoad(LazyImage l, ImageView i, boolean s){  
  130.             lazyImage=l;   
  131.             imageView=i;  
  132.             saveDisk = s;  
  133.         }  
  134.     }  
  135.       
  136.       
  137.     public void stopThread()  
  138.     {  
  139.         photoLoaderThread.interrupt();  
  140.     }  
  141.       
  142.     //stores list of photos to download   
  143.     class PhotosQueue  
  144.     {  
  145.         private Stack<PhotoToLoad> photosToLoadStack=new Stack<PhotoToLoad>();  
  146.           
  147.         //removes all instances of this ImageView   
  148.         public void clean(ImageView image)  
  149.         {  
  150.             for(int j=0 ;j<photosToLoadStack.size();){  
  151.                 if(photosToLoadStack.get(j).imageView==image)  
  152.                     photosToLoadStack.remove(j);  
  153.                 else  
  154.                     ++j;  
  155.             }  
  156.         }  
  157.     }  
  158.       
  159.     class PhotosLoaderThread extends Thread {  
  160.         public void run() {  
  161.             try {  
  162.                 while(true)  
  163.                 {  
  164.                     //thread waits until there are any images to load in the queue   
  165.                     if(photosQueue.photosToLoadStack.size()==0)  
  166.                         synchronized(photosQueue.photosToLoadStack){  
  167.                             photosQueue.photosToLoadStack.wait();  
  168.                         }  
  169.                     if(photosQueue.photosToLoadStack.size()!=0)  
  170.                     {  
  171.                         PhotoToLoad photoToLoad;  
  172.                         synchronized(photosQueue.photosToLoadStack){  
  173.                             photoToLoad=photosQueue.photosToLoadStack.pop();  
  174.                         }  
  175.                         Bitmap bmp=getBitmap(photoToLoad.lazyImage,photoToLoad.saveDisk);  
  176.                         memoryCache.put(photoToLoad.lazyImage.toFileName(), bmp);  
  177.                         String tag=imageViews.get(photoToLoad.imageView);  
  178.                         if(tag!=null && tag.equals(photoToLoad.lazyImage.toFileName())){  
  179.                             BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);  
  180.                             Activity a=(Activity)photoToLoad.imageView.getContext();  
  181.                             a.runOnUiThread(bd);  
  182.                         }  
  183.                     }  
  184.                     if(Thread.interrupted())  
  185.                         break;  
  186.                 }  
  187.             } catch (InterruptedException e) {  
  188.                 //allow thread to exit   
  189.             }  
  190.         }  
  191.     }  
  192.       
  193.       
  194.       
  195.     //Used to display bitmap in the UI thread   
  196.     class BitmapDisplayer implements Runnable  
  197.     {  
  198.         Bitmap bitmap;  
  199.         ImageView imageView;  
  200.         public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}  
  201.         public void run()  
  202.         {  
  203.             if(bitmap!=null)  
  204.                 imageView.setImageBitmap(bitmap);  
  205.             else  
  206.                 imageView.setImageResource(defaultImageResId);  
  207.         }  
  208.     }  
  209.   
  210.     public void clearCache() {  
  211.         memoryCache.clear();  
  212.         fileCache.clear();  
  213.     }  
  214.       
  215.     public void clearMemoryCache() {  
  216.         memoryCache.clear();  
  217.     }  
  218.   
  219. }  
  220.   
  221. class MemoryCache {  
  222.     private HashMap<String, SoftReference<Bitmap>> cache=new HashMap<String, SoftReference<Bitmap>>();  
  223.       
  224.     public Bitmap get(String id){  
  225.         if(!cache.containsKey(id))  
  226.             return null;  
  227.         SoftReference<Bitmap> ref=cache.get(id);  
  228.         return ref.get();  
  229.     }  
  230.       
  231.     public void put(String id, Bitmap bitmap){  
  232.         cache.put(id, new SoftReference<Bitmap>(bitmap));  
  233.     }  
  234.   
  235.     public void clear() {  
  236.         cache.clear();  
  237.     }  
  238. }  
  239.   
  240. class FileCache {  
  241.     
  242.     private File cacheDir;  
  243.       
  244.     public FileCache(Context context){  
  245.         //Find the dir to save cached images   
  246. //        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))   
  247.             cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"whyonly/cache");  
  248. //        else   
  249. //            cacheDir=context.getCacheDir();   
  250.         Log.d("ImageLoader","cacheDir:"+cacheDir);  
  251.         if(!cacheDir.exists())  
  252.             cacheDir.mkdirs();  
  253.     }  
  254.       
  255.     public File getFile(LazyImage lazyImage){     
  256.         String filename= lazyImage.toFileName();  
  257.         File f = new File(cacheDir, filename);  
  258.         return f;  
  259.           
  260.     }  
  261.       
  262.     public void clear(){  
  263.         File[] files=cacheDir.listFiles();  
  264.         for(File f:files)  
  265.             f.delete();  
  266.     }  
  267.   
  268. }  
package com.whyonly.core.wrapper;
import java.io.File;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.WeakHashMap;
import com.whyonly.core.bean.LazyImage;
import com.whyonly.core.util.ImageUtil;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import android.widget.ImageView;
public class ImageLoader {
private static final String TAG = "ImageLoader";
private MemoryCache memoryCache;
private FileCache fileCache;
private Map<ImageView, String> imageViews;
private PhotosLoaderThread photoLoaderThread;
private PhotosQueue photosQueue;
private int defaultImageResId;
//private Context context;
public ImageLoader(Context context,int defaultImageResId){
//Make the background thead low priority. So it will not affect the UI performance
photoLoaderThread=new PhotosLoaderThread();
photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);  
memoryCache=new MemoryCache();
fileCache=new FileCache(context);
imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
photosQueue=new PhotosQueue();
//this.context = context;
this.defaultImageResId = defaultImageResId;
}
public void displayImage(LazyImage lazyImage, ImageView imageView){
displayImage(lazyImage,imageView,true);
}
public void displayImage(LazyImage lazyImage, ImageView imageView,boolean saveDisk)
{
imageViews.put(imageView, lazyImage.toFileName());  
if(lazyImage.getImage()!=null){
imageView.setImageBitmap(lazyImage.getImage());//get from lazy image
//Log.d(TAG,"----LazyImage cache:"+lazyImage.toFileName());
}else{
Bitmap bitmap=memoryCache.get(lazyImage.toFileName());//get from memory cache
if(bitmap!=null){
lazyImage.setImage(bitmap);
imageView.setImageBitmap(bitmap);
//Log.d(TAG,"----MEMORY cache:"+lazyImage.toFileName());
}else
{
if(defaultImageResId>0)
imageView.setImageResource(defaultImageResId);
else
imageView.setImageBitmap(null);
if(lazyImage.getImage_url() != null)
queuePhoto(lazyImage, imageView,saveDisk);//get from SD card or web 
}    
}
}
private void queuePhoto(LazyImage lazyImage, ImageView imageView,boolean saveDisk)
{
//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 photosToLoad=new PhotoToLoad(lazyImage, imageView, saveDisk);
synchronized(photosQueue.photosToLoadStack){
photosQueue.photosToLoadStack.push(photosToLoad);
photosQueue.photosToLoadStack.notifyAll();
}
//start thread if it's not started yet
if(photoLoaderThread.getState()==Thread.State.NEW)
photoLoaderThread.start();
}
private Bitmap getBitmap(LazyImage lazyImage,boolean saveDisk) 
{
if(!saveDisk){
return ImageUtil.returnBitMap(lazyImage.getImage_url());
}
File f=fileCache.getFile(lazyImage);
//from SD cache
Bitmap b = ImageUtil.file2Bitmap(f);
if(b!=null){
lazyImage.setImage(b);
//Log.d(TAG,"----FILE cache:"+lazyImage.toFileName());
return b;
} 
//from web
try {
URL imageUrl = new URL(lazyImage.getImage_url());
HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
InputStream is=conn.getInputStream();
ImageUtil.inputStream2File(is, f);
lazyImage.setImage(ImageUtil.file2Bitmap(f));
//Log.d(TAG,"----WEB URL:"+lazyImage.toFileName());
return lazyImage.getImage();
} catch (Exception ex){
//ex.printStackTrace();
return null;
}
}
//Task for the queue
private class PhotoToLoad
{
public LazyImage lazyImage;
public ImageView imageView;
public boolean saveDisk;
public PhotoToLoad(LazyImage l, ImageView i, boolean s){
lazyImage=l; 
imageView=i;
saveDisk = s;
}
}
public void stopThread()
{
photoLoaderThread.interrupt();
}
//stores list of photos to download
class PhotosQueue
{
private Stack<PhotoToLoad> photosToLoadStack=new Stack<PhotoToLoad>();
//removes all instances of this ImageView
public void clean(ImageView image)
{
for(int j=0 ;j<photosToLoadStack.size();){
if(photosToLoadStack.get(j).imageView==image)
photosToLoadStack.remove(j);
else
++j;
}
}
}
class PhotosLoaderThread extends Thread {
public void run() {
try {
while(true)
{
//thread waits until there are any images to load in the queue
if(photosQueue.photosToLoadStack.size()==0)
synchronized(photosQueue.photosToLoadStack){
photosQueue.photosToLoadStack.wait();
}
if(photosQueue.photosToLoadStack.size()!=0)
{
PhotoToLoad photoToLoad;
synchronized(photosQueue.photosToLoadStack){
photoToLoad=photosQueue.photosToLoadStack.pop();
}
Bitmap bmp=getBitmap(photoToLoad.lazyImage,photoToLoad.saveDisk);
memoryCache.put(photoToLoad.lazyImage.toFileName(), bmp);
String tag=imageViews.get(photoToLoad.imageView);
if(tag!=null && tag.equals(photoToLoad.lazyImage.toFileName())){
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
}
}
}
//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(defaultImageResId);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
public void clearMemoryCache() {
memoryCache.clear();
}
}
class MemoryCache {
private HashMap<String, SoftReference<Bitmap>> cache=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();
}
}
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(),"whyonly/cache");
//        else
//            cacheDir=context.getCacheDir();
Log.d("ImageLoader","cacheDir:"+cacheDir);
if(!cacheDir.exists())
cacheDir.mkdirs();
}
public File getFile(LazyImage lazyImage){   
String filename= lazyImage.toFileName();
File f = new File(cacheDir, filename);
return f;
}
public void clear(){
File[] files=cacheDir.listFiles();
for(File f:files)
f.delete();
}
}

通过 ImageLoader的包装,应用起来应该非常简单,示例代码如下:

private ImageLoader imageLoader = new ImageLoader(this,R.drawable.nohead);

imageLoader.displayImage(bean, imageView);


以上给出来图片延迟加载的基本概念,以及通过一个示例,说明了如何封装,以便应用程序可以简单地调用。下一篇将通过一个完整的工程例子,对图片的延迟加载,以及ListView的延迟加载加载进行综述。(待续

  相关解决方案