热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android编程图片加载类ImageLoader定义与用法实例分析

这篇文章主要介绍了Android编程图片加载类ImageLoader定义与用法,结合实例形式分析了Android图片加载类ImageLoader的功能、定义、使用方法及相关操作注意事项,代码中备有较为详尽的注释便于理解,需要的朋友可以参考下

本文实例讲述了Android编程图片加载类ImageLoader定义与用法。分享给大家供大家参考,具体如下:

解析:

1)图片加载使用单例模式,避免多次调用时产生死锁
2)核心对象 LruCache

图片加载时先判断缓存里是否有图片,如果有,就使用缓存里的

没有就加载网络的,然后置入缓存

3)使用了线程池ExecutorService mThreadPool技术
4)使用了Semaphore 信号来控制变量按照先后顺序执行,避免空指针的问题

如何使用:

在Adapter里加载图片时

代码如下:
ImageLoader.getInstance.loadImage("http://www.baidu.com/images/kk.jpg", mImageView, true);

源码:

/**
 * @描述 图片加载类
 * @项目名称 App_News
 * @包名 com.android.news.tools
 * @类名 ImageLoader
 * @author chenlin
 * @date 2015-3-7 下午7:35:28
 * @version 1.0
 */
public class ImageLoader {
  private static ImageLoader mInstance;
  /**
   * 图片缓存的核心对象
   */
  private LruCache mLruCache;
  /**
   * 线程池
   */
  private ExecutorService mThreadPool;
  private static final int DEAFULT_THREAD_COUNT = 1;
  /**
   * 队列的调度方式
   */
  private Type mType = Type.LIFO;
  /**
   * 任务队列
   */
  private LinkedList mTaskQueue;
  /**
   * 后台轮询线程
   */
  private Thread mPoolThread;
  private Handler mPoolThreadHandler;
  /**
   * UI线程中的Handler
   */
  private Handler mUIHandler;
  private Semaphore mSemaphorePoolThreadHandler = new Semaphore(0);
  private Semaphore mSemaphoreThreadPool;
  private boolean isDiskCacheEnable = true;
  private static final String TAG = "ImageLoader";
  public enum Type {
    FIFO, LIFO;
  }
  private ImageLoader(int threadCount, Type type) {
    init(threadCount, type);
  }
  /**
   * 初始化
   *
   * @param threadCount
   * @param type
   */
  private void init(int threadCount, Type type) {
    initBackThread();
    // 获取我们应用的最大可用内存
    int maxMemory = (int) Runtime.getRuntime().maxMemory();
    int cacheMemory = maxMemory / 8;
    mLruCache = new LruCache(cacheMemory) {
      @Override
      protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
      }
    };
    // 创建线程池
    mThreadPool = Executors.newFixedThreadPool(threadCount);
    mTaskQueue = new LinkedList();
    mType = type;
    mSemaphoreThreadPool = new Semaphore(threadCount);
  }
  /**
   * 初始化后台轮询线程
   */
  private void initBackThread() {
    // 后台轮询线程
    mPoolThread = new Thread() {
      @Override
      public void run() {
        Looper.prepare();
        mPoolThreadHandler = new Handler() {
          @Override
          public void handleMessage(Message msg) {
            // 线程池去取出一个任务进行执行
            mThreadPool.execute(getTask());
            try {
              mSemaphoreThreadPool.acquire();
            } catch (InterruptedException e) {
            }
          }
        };
        // 释放一个信号量
        mSemaphorePoolThreadHandler.release();
        Looper.loop();
      };
    };
    mPoolThread.start();
  }
  public static ImageLoader getInstance() {
    if (mInstance == null) {
      synchronized (ImageLoader.class) {
        if (mInstance == null) {
          mInstance = new ImageLoader(DEAFULT_THREAD_COUNT, Type.LIFO);
        }
      }
    }
    return mInstance;
  }
  public static ImageLoader getInstance(int threadCount, Type type) {
    if (mInstance == null) {
      synchronized (ImageLoader.class) {
        if (mInstance == null) {
          mInstance = new ImageLoader(threadCount, type);
        }
      }
    }
    return mInstance;
  }
  /**
   * 根据path为imageview设置图片
   *
   * @param path
   * @param imageView
   */
  public void loadImage(final String path, final ImageView imageView, final boolean isFromNet) {
    imageView.setTag(path);
    if (mUIHandler == null) {
      mUIHandler = new Handler() {
        public void handleMessage(Message msg) {
          // 获取得到图片,为imageview回调设置图片
          ImgBeanHolder holder = (ImgBeanHolder) msg.obj;
          Bitmap bm = holder.bitmap;
          ImageView imageview = holder.imageView;
          String path = holder.path;
          // 将path与getTag存储路径进行比较
          if (imageview.getTag().toString().equals(path)) {
            imageview.setImageBitmap(bm);
          }
        };
      };
    }
    // 根据path在缓存中获取bitmap
    Bitmap bm = getBitmapFromLruCache(path);
    if (bm != null) {
      refreashBitmap(path, imageView, bm);
    } else {
      addTask(buildTask(path, imageView, isFromNet));
    }
  }
  /**
   * 根据传入的参数,新建一个任务
   *
   * @param path
   * @param imageView
   * @param isFromNet
   * @return
   */
  private Runnable buildTask(final String path, final ImageView imageView, final boolean isFromNet) {
    return new Runnable() {
      @Override
      public void run() {
        Bitmap bm = null;
        if (isFromNet) {
          File file = getDiskCacheDir(imageView.getContext(), md5(path));
          if (file.exists())// 如果在缓存文件中发现
          {
            Log.e(TAG, "find image :" + path + " in disk cache .");
            bm = loadImageFromLocal(file.getAbsolutePath(), imageView);
          } else {
            if (isDiskCacheEnable)// 检测是否开启硬盘缓存
            {
              boolean downloadState = DownloadImgUtils.downloadImgByUrl(path, file);
              if (downloadState)// 如果下载成功
              {
                Log.e(TAG,
                    "download image :" + path + " to disk cache . path is "
                        + file.getAbsolutePath());
                bm = loadImageFromLocal(file.getAbsolutePath(), imageView);
              }
            } else
            // 直接从网络加载
            {
              Log.e(TAG, "load image :" + path + " to memory.");
              bm = DownloadImgUtils.downloadImgByUrl(path, imageView);
            }
          }
        } else {
          bm = loadImageFromLocal(path, imageView);
        }
        // 3、把图片加入到缓存
        addBitmapToLruCache(path, bm);
        refreashBitmap(path, imageView, bm);
        mSemaphoreThreadPool.release();
      }
    };
  }
  private Bitmap loadImageFromLocal(final String path, final ImageView imageView) {
    Bitmap bm;
    // 加载图片
    // 图片的压缩
    // 1、获得图片需要显示的大小
    ImageSize imageSize = ImageSizeUtil.getImageViewSize(imageView);
    // 2、压缩图片
    bm = decodeSampledBitmapFromPath(path, imageSize.width, imageSize.height);
    return bm;
  }
  /**
   * 从任务队列取出一个方法
   *
   * @return
   */
  private Runnable getTask() {
    if (mType == Type.FIFO) {
      return mTaskQueue.removeFirst();
    } else if (mType == Type.LIFO) {
      return mTaskQueue.removeLast();
    }
    return null;
  }
  /**
   * 利用签名辅助类,将字符串字节数组
   *
   * @param str
   * @return
   */
  public String md5(String str) {
    byte[] digest = null;
    try {
      MessageDigest md = MessageDigest.getInstance("md5");
      digest = md.digest(str.getBytes());
      return bytes2hex02(digest);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }
    return null;
  }
  /**
   * 方式二
   *
   * @param bytes
   * @return
   */
  public String bytes2hex02(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    String tmp = null;
    for (byte b : bytes) {
      // 将每个字节与0xFF进行与运算,然后转化为10进制,然后借助于Integer再转化为16进制
      tmp = Integer.toHexString(0xFF & b);
      if (tmp.length() == 1)// 每个字节8为,转为16进制标志,2个16进制位
      {
        tmp = "0" + tmp;
      }
      sb.append(tmp);
    }
    return sb.toString();
  }
  private void refreashBitmap(final String path, final ImageView imageView, Bitmap bm) {
    Message message = Message.obtain();
    ImgBeanHolder holder = new ImgBeanHolder();
    holder.bitmap = bm;
    holder.path = path;
    holder.imageView = imageView;
    message.obj = holder;
    mUIHandler.sendMessage(message);
  }
  /**
   * 将图片加入LruCache
   *
   * @param path
   * @param bm
   */
  protected void addBitmapToLruCache(String path, Bitmap bm) {
    if (getBitmapFromLruCache(path) == null) {
      if (bm != null)
        mLruCache.put(path, bm);
    }
  }
  /**
   * 根据图片需要显示的宽和高对图片进行压缩
   *
   * @param path
   * @param width
   * @param height
   * @return
   */
  protected Bitmap decodeSampledBitmapFromPath(String path, int width, int height) {
    // 获得图片的宽和高,并不把图片加载到内存中
    BitmapFactory.Options optiOns= new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(path, options);
    options.inSampleSize = ImageSizeUtil.caculateInSampleSize(options, width, height);
    // 使用获得到的InSampleSize再次解析图片
    options.inJustDecodeBounds = false;
    Bitmap bitmap = BitmapFactory.decodeFile(path, options);
    return bitmap;
  }
  private synchronized void addTask(Runnable runnable) {
    mTaskQueue.add(runnable);
    // if(mPoolThreadHandler==null)wait();
    try {
      if (mPoolThreadHandler == null)
        mSemaphorePoolThreadHandler.acquire();
    } catch (InterruptedException e) {
    }
    mPoolThreadHandler.sendEmptyMessage(0x110);
  }
  /**
   * 获得缓存图片的地址
   *
   * @param context
   * @param uniqueName
   * @return
   */
  public File getDiskCacheDir(Context context, String uniqueName) {
    String cachePath;
    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
      cachePath = context.getExternalCacheDir().getPath();
    } else {
      cachePath = context.getCacheDir().getPath();
    }
    return new File(cachePath + File.separator + uniqueName);
  }
  /**
   * 根据path在缓存中获取bitmap
   *
   * @param key
   * @return
   */
  private Bitmap getBitmapFromLruCache(String key) {
    return mLruCache.get(key);
  }
  private class ImgBeanHolder {
    Bitmap bitmap;
    ImageView imageView;
    String path;
  }
}

相关工具类:

/**
 * @描述 获取图片大小工具类s
 * @项目名称 App_News
 * @包名 com.android.news.util
 * @类名 ImageSizeUtil
 * @author chenlin
 * @date 2014-3-7 下午7:37:50
 * @version 1.0
 */
public class ImageSizeUtil {
  /**
   * 根据需求的宽和高以及图片实际的宽和高计算SampleSize
   *
   * @param options
   * @param width
   * @param height
   * @return
   */
  public static int caculateInSampleSize(Options options, int reqWidth, int reqHeight) {
    int width = options.outWidth;
    int height = options.outHeight;
    int inSampleSize = 1;
    if (width > reqWidth || height > reqHeight) {
      int widthRadio = Math.round(width * 1.0f / reqWidth);
      int heightRadio = Math.round(height * 1.0f / reqHeight);
      inSampleSize = Math.max(widthRadio, heightRadio);
    }
    return inSampleSize;
  }
  /**
   * 根据ImageView获适当的压缩的宽和高
   *
   * @param imageView
   * @return
   */
  public static ImageSize getImageViewSize(ImageView imageView) {
    ImageSize imageSize = new ImageSize();
    DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();
    LayoutParams lp = imageView.getLayoutParams();
    int width = imageView.getWidth();// 获取imageview的实际宽度
    if (lp != null) {
      if (width <= 0) {
        width = lp.width;// 获取imageview在layout中声明的宽度
      }
    }
    if (width <= 0) {
      // width = imageView.getMaxWidth();// 检查最大值
      width = getImageViewFieldValue(imageView, "mMaxWidth");
    }
    if (width <= 0) {
      width = displayMetrics.widthPixels;
    }
    int height = imageView.getHeight();// 获取imageview的实际高度
    if (lp != null) {
      if (height <= 0) {
        height = lp.height;// 获取imageview在layout中声明的宽度
      }
    }
    if (height <= 0) {
      height = getImageViewFieldValue(imageView, "mMaxHeight");// 检查最大值
    }
    if (height <= 0) {
      height = displayMetrics.heightPixels;
    }
    imageSize.width = width;
    imageSize.height = height;
    return imageSize;
  }
  public static class ImageSize {
    public int width;
    public int height;
  }
  /**
   * 通过反射获取imageview的某个属性值
   *
   * @param object
   * @param fieldName
   * @return
   */
  private static int getImageViewFieldValue(Object object, String fieldName) {
    int value = 0;
    try {
      Field field = ImageView.class.getDeclaredField(fieldName);
      field.setAccessible(true);
      int fieldValue = field.getInt(object);
      if (fieldValue > 0 && fieldValue 

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android图形与图像处理技巧总结》、《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》

希望本文所述对大家Android程序设计有所帮助。


推荐阅读
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Echarts图表重复加载、axis重复多次请求问题解决记录
    文章目录1.需求描述2.问题描述正常状态:问题状态:3.解决方法1.需求描述使用Echats实现了一个中国地图:通过选择查询周期&#x ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • Python字典推导式及循环列表生成字典方法
    本文介绍了Python中使用字典推导式和循环列表生成字典的方法,包括通过循环列表生成相应的字典,并给出了执行结果。详细讲解了代码实现过程。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • “你永远都不知道明天和‘公司的意外’哪个先来。”疫情期间,这是我们最战战兢兢的心情。但是显然,有些人体会不了。这份行业数据,让笔者“柠檬” ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
author-avatar
别被风景迷了眼
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有