Android实现异步加载图片

作者:赵大海 时间:2021-08-14 16:44:20 

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

今天在做项目的时候,有一个实现异步加载图片的功能,虽然比较简单但还是记录一下吧.因为麦洛之前实现异步加载图片都是使用了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,异步加载
0
投稿

猜你喜欢

  • 一文彻底搞懂Java和JDK的版本命名问题

    2023-11-24 01:39:25
  • Unity实现Flappy Bird游戏开发实战

    2023-03-10 19:56:58
  • Java数据结构之图的路径查找算法详解

    2023-03-27 03:47:11
  • SpringBoot常用注解详细整理

    2022-12-15 18:53:01
  • Java乱码问题解决方法_动力节点Java学院整理

    2021-07-25 01:04:52
  • springboot 整合fluent mybatis的过程,看这篇够了

    2022-09-14 01:16:53
  • mybatis如何批量添加一对多中间表

    2021-08-26 03:16:41
  • java实现选课系统

    2021-08-15 07:43:44
  • java清除u盘内存卡里的垃圾文件示例

    2023-05-12 06:41:38
  • 如何利用反射构建元数据查看器

    2021-05-27 00:11:05
  • java 装饰模式(Decorator Pattern)详解及实例代码

    2023-09-07 03:13:08
  • Jenkins Host key verification failed问题解决

    2021-07-14 12:53:41
  • Jackson序列化和反序列化忽略字段操作

    2022-08-29 14:01:14
  • Java截取字符串的几种方法示例

    2023-11-29 12:36:32
  • Springboot2以代码的方式统一配置Jackson教程

    2021-08-30 16:47:33
  • 利用C#实现最基本的小说爬虫示例代码

    2023-09-25 15:48:17
  • Springboot jar主清单属性丢失解决方案

    2022-04-06 05:30:26
  • Spring循环依赖代码演示及解决方案

    2022-05-17 01:56:08
  • Idea自动生成Entity实现过程详解

    2022-06-03 12:18:24
  • Java多线程编程小实例模拟停车场系统

    2022-07-07 06:36:29
  • asp之家 软件编程 m.aspxhome.com