今天在做项目的时候,有一个实现异步加载图片的功能,虽然比较简单但还是记录一下吧.因为麦洛之前实现异步加载图片都是使用了AsynTask这个API,继续这个类,实现起来非常简单也很方便.在doInBackground()方法里实现下载逻辑.具体实现如下

实现逻辑是:先从内存中读取,如果内存中有这张图片,则直接使用;如果内存没有再到sdcard上读取,如果有则显示;如果sdcard上还没有则到网络上读取.内存中开启缓存是参考了网上的实现.麦洛在这里非常感谢喜欢分享的程序猿们.

public class ImageDownloader extends AsyncTask {

    private static final String TAG = “ImageDownloader”;

    // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)

    private Map> imageCache = new HashMap>();

    /**

     * 显示图片的控件

     */

    private ImageView mImageView;

    public ImageDownloader(ImageView image) {

       mImageView = image;

    }

    @Override

    protected Object doInBackground(String… params) {

       // Log.i(“ImageDownloader”, “loading image…”);

       String url = params[0];

       Drawable drawable = null;

       try {

           if (!”".equals(url) && url != null) {

              String fileName = url.hashCode()+”.jpg”;

              // 如果缓存过就从缓存中取出数据

              if (imageCache.containsKey(fileName)) {

                  SoftReference softReference = imageCache.get(fileName);

                  drawable = softReference.get();

                  if (drawable != null) {

                     return drawable;

                  }

              }

              File dir = new File(FileConstant.IMAGE_FILE_PATH);

              if (!dir.exists()) {

                  boolean m = dir.mkdirs();

              }

              File file = new File(dir, fileName);

              if (file.exists() && file.length() > 0) {

                  Log.i(TAG, “load image from sd card”);

                  // 如果文件存在则直接读取sdcard

                  drawable = readFromSdcard(file);

              } else {

                  //file.createNewFile();

                  Log.i(TAG, “load image from network”);

                  URL imageUrl = new URL(url);

                  // 写入sdcard

                  if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

                     saveImageFile(imageUrl, file);

                     drawable = Drawable.createFromStream(new FileInputStream(file), fileName);

                  }else{

                     //直接从流读取

                     drawable = Drawable.createFromStream(imageUrl.openStream(), fileName);

                  }

              }

              if(drawable!=null){

                  //保存在缓存中

                  imageCache.put(fileName, new SoftReference(drawable));

              }

           }

       } catch (Exception e) {

           e.printStackTrace();

       }

       return drawable;

    }

    private void saveImageFile(URL url, File file) {

       FileOutputStream out = null;

       InputStream in = null;

       try {

           file.deleteOnExit();

           out = new FileOutputStream(file);

           in = url.openStream();

           byte[] buf = new byte[1024];

           int len = -1;

           while((len = in.read(buf))!=-1){

              out.write(buf, 0, len);

              out.flush();

           }

       } catch (Exception e) {

           e.printStackTrace();

       } finally {

           if(out!=null){

              try {

                  out.close();

              } catch (IOException e) {

                  e.printStackTrace();

              }

           }

           if(in!=null){

              try {

                  in.close();

              } catch (IOException e) {

                  e.printStackTrace();

              }

           }

       }

    }

  private Drawable readFromSdcard(File file) throws Exception {

       FileInputStream in = new FileInputStream(file);

       return Drawable.createFromStream(in, file.getName());

    }

    @Override

    protected void onPostExecute(Object result) {

       super.onPostExecute(result);

       Drawable drawable = (Drawable) result;

       if (mImageView != null && drawable != null) {

           mImageView.setBackgroundDrawable(drawable);

       }

    }

}

 使用时:

ImageDownloader loader = new ImageDownloader(imageView);
loader.execute(url);
其实这样的话,还有一些隐患的,就是说这个类实现还是有些问题的.比如每次都在imageView中设置网络上的图片时,其实是没有使用到这个类里面的内存缓存的,就是imageCache
Map> imageCache = new HashMap>();
因为每次设置imageView的时候,都是new了一个ImageDownloader的对象.所以每个ImageDownloader对象里面都是独立的一个imageCache.
      另外,AsynTask也是一个线程.而每次使用都开一个线程来load 图片,对线程个数没有进行显示,毕竟线程数目还是有限制的.
所以麦洛今天发现了这个问题,于是参考了别人的实现,使用了线程池,实现逻辑也上面的代码一样,先从内存读取,如果没有到sdcard读取,如果还是没有,则是网络读取;实现没有使用AsynTask,具体代码如下:

public class ImageDownloader2 {

    // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)

    private Map> imageCache = new HashMap>();

    // 固定五个线程来执行任务

    private ExecutorService executorService = Executors.newFixedThreadPool(5);

    private final Handler handler = new Handler();

    public Drawable loadDrawable(final String imageUrl, final ImageCallback callback) {

       Drawable drawable = null;

       final String fileName = imageUrl.hashCode() + “.jpg”;

       // 如果缓存过就从缓存中取出数据

       if (imageCache.containsKey(fileName)) {

           SoftReference softReference = imageCache.get(fileName);

           drawable = softReference.get();

           if (drawable != null) {

              callback.imageLoaded(drawable);

              return drawable;

           }

       }

       // 缓存中没有图像,则从网络或者文件中上取出数据,并将取出的数据缓存到内存中

       executorService.submit(new LoadTask(fileName,imageUrl,callback));

       return null;

    }

    /**

     * 从网络或者sdcard加载图片

    */

    class LoadTask implements Runnable {

        String fileName;

       String imageUrl;

       Drawable drawable = null;

       ImageCallback callback;

       public LoadTask(String fileName,String imageUrl,ImageCallback callback){

           this.fileName = fileName;

           this.imageUrl = imageUrl;

           this.callback = callback;

       }

       public void run() {

           try {

              // 从文件中读取

              File dir = new File(FileConstant.IMAGE_FILE_PATH);

              if (!dir.exists()) {

                  dir.mkdirs();

              }

              File file = new File(dir, fileName);

              if (file.exists() && file.length() > 0) {

                  // 如果文件存在则直接读取sdcard

                  drawable = readFromSdcard(file);

              } else {

                  drawable = loadImageFromUrl(imageUrl);

              }

               imageCache.put(fileName, new SoftReference(drawable));

               handler.post(new Runnable() {

                  public void run() {

                     callback.imageLoaded(drawable);

                  }

              });

           } catch (Exception e) {

              throw new RuntimeException(e);

           }

       }

    }

   private Drawable readFromSdcard(File file) {

       try {

           FileInputStream in = new FileInputStream(file);

           return Drawable.createFromStream(in, file.getName());

       } catch (FileNotFoundException e) {

           e.printStackTrace();

       }

       return null;

    }

    // 从网络上取数据方法

    protected Drawable loadImageFromUrl(String imageUrl) {

       try {

           Drawable drawable = null;

           URL url = new URL(imageUrl);

           drawable = Drawable.createFromStream(url.openStream(), “image.jpg”);

           return drawable;

       } catch (Exception e) {

           throw new RuntimeException(e);

       }

    }

     /**

     * 对外界开放的回调接口

    */

    public interface ImageCallback {

       // 注意 此方法是用来设置目标对象的图像资源

       public void imageLoaded(Drawable imageDrawable);

    }

}

这个ImageDownloader2的使用也很简单

public class ImageUtil {

 static ImageDownloader2 loader = null;

   public static void loadImage(String url,final ImageView imageView){

       if(loader == null){

           ImageDownloader loader = new ImageDownloader(imageView);

           loader.execute(url);

           loader = new ImageDownloader2();

       }

       loader.loadDrawable(url, new ImageCallback() {

           @Override

           public void imageLoaded(Drawable imageDrawable) {

              if(imageDrawable!=null){

                  imageView.setBackgroundDrawable(imageDrawable);

              }

           }

       });

    }

}

每次在使用是需要调用ImageUtil.loadImage(url,imageView)将图片url已经需要显示图片的控件ImageView的引用传入就可以了.