Android实现异步加载图片

麦洛开通博客以来,有一段时间没有更新博文了.主要是麦洛这段时间因项目开发实在太忙了.今天周六还在公司加班,苦逼程序猿都是这样生活的.

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

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

public class ImageDownloader extends AsyncTask<String, Integer, Object> {  private static final String TAG = "ImageDownloader";  // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)  private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();  /**   * 显示图片的控件   */  private ImageView mImageView;  public ImageDownloader(ImageView image) {    mImageView = image;  }  @Override  protected void onPreExecute() {    super.onPreExecute();  }  @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<Drawable> 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>(drawable));        }      }    } catch (Exception e) {      e.printStackTrace();    }     return drawable;  }  /**   * save image*/  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();        }      }    }  }  /**   * 从sdcard中获取图片*/  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);    }  }  @Override  protected void onProgressUpdate(Integer... values) {    super.onProgressUpdate(values);  }  @Override  protected void onCancelled() {    super.onCancelled();  }}

使用时:

ImageDownloader loader = new ImageDownloader(imageView);loader.execute(url);

其实这样的话,还有一些隐患的,就是说这个类实现还是有些问题的.比如每次都在imageView中设置网络上的图片时,其实是没有使用到这个类里面的内存缓存的,就是imageCache

Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();因为每次设置imageView的时候,都是new了一个ImageDownloader的对象.所以每个ImageDownloader对象里面都是独立的一个imageCache.

另外,AsynTask也是一个线程.而每次使用都开一个线程来load 图片,对线程个数没有进行显示,毕竟线程数目还是有限制的.所以麦洛今天发现了这个问题,于是参考了别人的实现,使用了线程池,实现逻辑也上面的代码一样,先从内存读取,如果没有到sdcard读取,如果还是没有,则是网络读取;实现没有使用AsynTask,具体代码如下:

/** * 异步加载图片,并将图片设置到ImageView控件中*/public class ImageDownloader extends AsyncTask<String, Integer, Object> {  private static final String TAG = "ImageDownloader";  // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)  private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();  /**   * 显示图片的控件   */  private ImageView mImageView;  public ImageDownloader(ImageView image) {    mImageView = image;  }  @Override  protected void onPreExecute() {    super.onPreExecute();  }  @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<Drawable> 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>(drawable));        }      }    } catch (Exception e) {      e.printStackTrace();    }     return drawable;  }  /**   * save image*/  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();        }      }    }  }  /**   * 从sdcard中获取图片 */  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);    }  }  @Override  protected void onProgressUpdate(Integer... values) {    super.onProgressUpdate(values);  }  @Override  protected void onCancelled() {    super.onCancelled();  }}

这个ImageDownloader2的使用也很简单

public class ImageUtil {  /**   * image loader   */  static ImageDownloader2 loader = null;    /**   * load image*/  public static void loadImage(String url,final ImageView imageView){    if(loader == null){      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的引用传入就可以了.

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

才能做到人在旅途,感悟人生,享受人生。

Android实现异步加载图片

相关文章:

你感兴趣的文章:

标签云: