总体思路

Handler + looper + message

核心类

  1. package com.base.imagechoose.util;
  2.  
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.os.Handler;
  6. import android.os.Looper;
  7. import android.os.Message;
  8. import android.support.v4.util.LruCache;
  9. import android.util.DisplayMetrics;
  10. import android.view.ViewGroup;
  11. import android.widget.ImageView;
  12.  
  13. import java.lang.reflect.Field;
  14. import java.util.LinkedList;
  15. import java.util.concurrent.ExecutorService;
  16. import java.util.concurrent.Executors;
  17. import java.util.concurrent.Semaphore;
  18.  
  19. /**
  20. * 图片加载类
  21. */
  22. public class ImageLoader {
  23.  
  24. private static ImageLoader mInstance;
  25.  
  26. /**
  27. * 图片缓存
  28. */
  29. private LruCache<String, Bitmap> mLruCache;
  30.  
  31. private ExecutorService mThreadPool;//线程池, 加载任务
  32.  
  33. private static final int DAFULT_THREAD_COUNT = 1; //线程数量
  34. /**
  35. * 队列调度方式
  36. */
  37. private Type mType = Type.FIFO;
  38.  
  39. /**
  40. * 任务队列
  41. */
  42. private LinkedList<Runnable> mTaskQueue;
  43.  
  44. /**
  45. * 后台轮询线程
  46. */
  47. private Thread mPoolThread;
  48. private Handler mPoolTheadHandler;
  49.  
  50. //信号量同步mPoolTheadHandler
  51. private Semaphore mSemaphorePoolTheadHandler = new Semaphore(0);
  52. /**
  53. * 用Semaphore来控制一个线程执行完才执行下一个
  54. */
  55. private Semaphore mSemaphorePool; //用信号量来达到线程Type规则
  56.  
  57. /**
  58. * UI线程中的Handler
  59. */
  60. private Handler mUIHandler;
  61.  
  62. //先进先出
  63. public enum Type {
  64. FIFO, FIFI;
  65. }
  66.  
  67. private ImageLoader(int threadCount, Type type) {
  68. init(threadCount, type);
  69. }
  70.  
  71. public static ImageLoader getInstance() {
  72. if (mInstance == null) {
  73. synchronized (ImageLoader.class) {
  74. if (mInstance == null) {
  75. mInstance = new ImageLoader(DAFULT_THREAD_COUNT, Type.FIFO);
  76. }
  77. }
  78. }
  79. return mInstance;
  80. }
  81.  
  82. public static ImageLoader getInstance(int threadCount, Type type) {
  83. if (mInstance == null) {
  84. synchronized (ImageLoader.class) {
  85. if (mInstance == null) {
  86. mInstance = new ImageLoader(threadCount, type);
  87. }
  88. }
  89. }
  90. return mInstance;
  91. }
  92.  
  93. private void init(int threadCount, Type type) {
  94. //后台轮询线程
  95. mPoolThread = new Thread(){
  96.  
  97. @Override
  98. public void run() {
  99. Looper.prepare();
  100. mPoolTheadHandler = new Handler() {
  101. @Override
  102. public void handleMessage(Message msg) {
  103. //线程池去取出一个任务进行执行
  104. mThreadPool.execute(getTask());
  105.  
  106. try {
  107. //如果信号量为3,到第4个就会阻塞住
  108. mSemaphorePool.acquire();
  109. } catch (InterruptedException e) {
  110. e.printStackTrace();
  111. }
  112. }
  113. };
  114. //释放一个信号量
  115. mSemaphorePoolTheadHandler.release();
  116. Looper.loop();
  117. }
  118. };
  119. mPoolThread.start();
  120.  
  121. //获取应用的最大可用内存
  122. int maxMemory = (int) Runtime.getRuntime().maxMemory();
  123. //设置缓存空间
  124. int cacheMemory = maxMemory/8;
  125.  
  126. //初始化缓存
  127. mLruCache = new LruCache<String, Bitmap>(cacheMemory) {
  128. @Override
  129. protected int sizeOf(String key, Bitmap value) {
  130. //返回内存,value每一行的大小*高度
  131. return value.getRowBytes()*value.getHeight();
  132. }
  133. };
  134.  
  135. //创建线程池
  136. mThreadPool = Executors.newFixedThreadPool(threadCount);
  137. //初始化任务队列
  138. mTaskQueue = new LinkedList<>();
  139.  
  140. mType = type;
  141.  
  142. mSemaphorePool = new Semaphore(threadCount);
  143. }
  144.  
  145. /**
  146. * 从任务队列取出一个方法
  147. * @return
  148. */
  149. private Runnable getTask() {
  150.  
  151. if (mType == Type.FIFO) {
  152. return mTaskQueue.removeFirst();
  153. } else {
  154. return mTaskQueue.removeLast();
  155. }
  156.  
  157. }
  158.  
  159. /**
  160. * 根据path为控件加载图片
  161. * @param path 路径
  162. * @param imageView 控件
  163. */
  164. public void loadImage(final String path, final ImageView imageView) {
  165.  
  166. imageView.setTag(path);
  167.  
  168. if (mUIHandler == null) {
  169. mUIHandler = new Handler() {
  170. @Override
  171. public void handleMessage(Message msg) {
  172. //获取得到的图片并在控件上显示
  173. ImgBeanHolder holder = (ImgBeanHolder) msg.obj;
  174.  
  175. //当tag为传入path时才进行赋值
  176. if (holder.imageView.getTag().equals(holder.path)) {
  177. holder.imageView.setImageBitmap(holder.bm);
  178. }
  179. }
  180. };
  181. }
  182.  
  183. //根据path在缓存中获取图片
  184. Bitmap bm = getBitmapFromLruCache(path);
  185.  
  186. if (bm != null) {
  187. refreashBitmap(path, imageView, bm);
  188. } else {
  189. //没有图片,添加一个任务
  190. addTasks(new Runnable(){
  191. @Override
  192. public void run() {
  193. //加载图片
  194. //图片压缩
  195. //1.获得图片需要显示的大小
  196. ImageSize imageSize = getImageViewSize(imageView);
  197. //2.压缩图片
  198. Bitmap bm = decodeSampleBitmapFromPath(path, imageSize.width, imageSize.height);
  199.  
  200. //3.把图片加入缓存
  201. addBitmapToLruCache(path,bm);
  202.  
  203. refreashBitmap(path, imageView, bm);
  204. //当前任务执行完后释放信号量
  205. mSemaphorePool.release();
  206. }
  207. });
  208. }
  209.  
  210. }
  211.  
  212. /**
  213. * 发送handler显示图片
  214. * @param path
  215. * @param imageView
  216. * @param bm
  217. */
  218. private void refreashBitmap(String path, ImageView imageView, Bitmap bm) {
  219. Message message = new Message();
  220. ImgBeanHolder imgBeanHolder = new ImgBeanHolder();
  221. imgBeanHolder.bm = bm;
  222. imgBeanHolder.imageView = imageView;
  223. imgBeanHolder.path = path;
  224. message.obj = imgBeanHolder;
  225. mUIHandler.sendMessage(message);
  226. }
  227.  
  228. /**
  229. * 将图片放入缓存
  230. * @param path
  231. * @param bm
  232. */
  233. private void addBitmapToLruCache(String path, Bitmap bm) {
  234. if(getBitmapFromLruCache(path) == null) {
  235. if (bm != null) {
  236. mLruCache.put(path, bm);
  237. }
  238. }
  239.  
  240. }
  241.  
  242. /**
  243. * 添加任务
  244. * @param runnable
  245. */
  246. private synchronized void addTasks(Runnable runnable) {
  247. //将任务放入任务队列中
  248. mTaskQueue.add(runnable);
  249. //发送一个通知去通知后台轮询执行任务
  250. try {
  251. if (mPoolTheadHandler == null)
  252. mSemaphorePoolTheadHandler.acquire();
  253. } catch (InterruptedException e) {
  254. e.printStackTrace();
  255. }
  256. mPoolTheadHandler.sendEmptyMessage(0x110);
  257. }
  258.  
  259. /**
  260. * 根据path在缓存中获取图片
  261. * @param key
  262. * @return
  263. */
  264. private Bitmap getBitmapFromLruCache(String key) {
  265.  
  266. return mLruCache.get(key);
  267. }
  268.  
  269. /**
  270. * 根据imageview获取图片需要压缩的宽和高
  271. * @param imageView
  272. */
  273. private ImageSize getImageViewSize(ImageView imageView) {
  274. ImageSize imageSize = new ImageSize();
  275.  
  276. DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();
  277. ViewGroup.LayoutParams lp = imageView.getLayoutParams();
  278.  
  279. int width = imageView.getWidth();
  280.  
  281. if (width <= 0) {
  282. width = lp.width; //获取imageview在layout中声明的亮度
  283. }
  284.  
  285. if (width <= 0) {
  286. width = getImageViewFileValue(imageView, "mMaxWidth"); //检查最大值
  287. }
  288.  
  289. if (width <= 0) {
  290. width = displayMetrics.widthPixels;
  291. }
  292.  
  293. int height = imageView.getHeight();
  294.  
  295. if (height <= 0) {
  296. height = lp.height; //获取imageview在layout中声明的亮度
  297. }
  298.  
  299. if (height <= 0) {
  300. height = getImageViewFileValue(imageView, "mMaxHeight"); //检查最大值 //检查最大值
  301. }
  302.  
  303. if (height <= 0) {
  304. height = displayMetrics.heightPixels;
  305. }
  306.  
  307. imageSize.width = width;
  308. imageSize.height = height;
  309.  
  310. return imageSize;
  311. }
  312.  
  313. private static int getImageViewFileValue(Object object,String fileName) {
  314. int value = 0;
  315.  
  316. //得到当前ImageView的字段集合
  317. try {
  318. Field field = ImageView.class.getDeclaredField(fileName);
  319. field.setAccessible(true);
  320.  
  321. int fieldValue = field.getInt(object);
  322.  
  323. if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
  324. value = fieldValue;
  325. }
  326. } catch (Exception e) {
  327. e.printStackTrace();
  328. }
  329.  
  330. return value;
  331. }
  332.  
  333. /**
  334. * 根据图片需要的宽高对图片进行压缩
  335. * @param path
  336. * @param width
  337. * @param height
  338. * @return
  339. */
  340. private Bitmap decodeSampleBitmapFromPath(String path, int width, int height) {
  341.  
  342. BitmapFactory.Options options = new BitmapFactory.Options();
  343. //不加入内存
  344. options.inJustDecodeBounds = true;
  345. //获取实际的options
  346. BitmapFactory.decodeFile(path, options);
  347.  
  348. //用实际的options和需要显示的宽高算出inSampleSize
  349. options.inSampleSize = caculateInSampleSize(options, width, height);
  350.  
  351. //使用获取的inSampleSize再次解析图片
  352. //放入内存
  353. options.inJustDecodeBounds = false;
  354.  
  355. Bitmap bitmap = BitmapFactory.decodeFile(path, options);
  356.  
  357. return bitmap;
  358. }
  359.  
  360. /**
  361. * 用实际的options和需要显示的宽高算出inSampleSize
  362. * @param options
  363. * @param reqWidth
  364. * @param reqHeight
  365. * @return
  366. */
  367. private int caculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
  368.  
  369. int width = options.outWidth;
  370. int height = options.outHeight;
  371.  
  372. int inSampleSize = 1;
  373.  
  374. if (width > reqHeight || height > reqHeight) {
  375. int widthRadio = Math.round(width*1.0f/reqWidth);
  376. int heightRadio = Math.round(height*1.0f/reqHeight);
  377.  
  378. inSampleSize = Math.max(widthRadio, heightRadio);
  379. }
  380.  
  381. return inSampleSize;
  382. }
  383.  
  384. private class ImageSize{
  385. int width;
  386. int height;
  387. }
  388.  
  389. private class ImgBeanHolder {
  390. Bitmap bm;
  391. String path;
  392. ImageView imageView;
  393. }
  394.  
  395. }

  

Android list加载图片工具类的更多相关文章

  1. android异步加载图片并缓存到本地实现方法

    图片过多造成内存溢出,这个是最不容易解决的,要想一些好的缓存策略,比如大图片使用LRU缓存策略或懒加载缓存策略.今天首先介绍一下本地缓存图片     在android项目中访问网络图片是非常普遍性的事 ...

  2. android listview 加载图片错乱(错位)

       写道 今天晚上一个朋友介绍我看了一篇文章,也是解决android中listview在加载图片错位的问题,看了之后,感觉写的很好,自己也遇到这个问题,但是又不知道从何下手,看到这篇文章后,我的问题 ...

  3. Android Glide加载图片时转换为圆形、圆角、毛玻璃等图片效果

     Android Glide加载图片时转换为圆形.圆角.毛玻璃等图片效果 附录1简单介绍了Android开源的图片加载框架.在实际的开发中,虽然Glide解决了快速加载图片的问题,但还有一个问题悬 ...

  4. 演化理解 Android 异步加载图片(转)

    演化理解 Android 异步加载图片(转)http://www.cnblogs.com/CJzhang/archive/2011/10/20/2218474.html

  5. Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅

      Android 高手进阶(21)  版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请注明出处http://blog.csdn.net/xiaanming/article/details ...

  6. android 网络加载图片,对图片资源进行优化,并且实现内存双缓存 + 磁盘缓存

    经常会用到 网络文件 比如查看大图片数据 资源优化的问题,当然用开源的项目  Android-Universal-Image-Loader  或者 ignition 都是个很好的选择. 在这里把原来 ...

  7. 实例演示Android异步加载图片

    本文给大家演示异步加载图片的分析过程.让大家了解异步加载图片的好处,以及如何更新UI.首先给出main.xml布局文件:简单来说就是 LinearLayout 布局,其下放了2个TextView和5个 ...

  8. 实例演示Android异步加载图片(转)

    本文给大家演示异步加载图片的分析过程.让大家了解异步加载图片的好处,以及如何更新UI.首先给出main.xml布局文件:简单来说就是 LinearLayout 布局,其下放了2个TextView和5个 ...

  9. [Android]异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3574131.html  这个可以实现ImageView异步加载 ...

随机推荐

  1. Entity Framework Code-First(9):DataAnnotations

    DataAnnotations in Code-First: EF Code-First provides a set of DataAnnotation attributes, which you ...

  2. Linux的.run文件简单制作

    run程序安装包实质上是一个安装脚本加要安装的程序,如下图所示: |-----------------|| || 安装脚本 || ||-----------------|| || 程序 || ||-- ...

  3. 对Json的一些理解

    标准json格式:{"name":"王大昭","url":"https://www.cnblogs.com/codezhao/&q ...

  4. sqlserver2012——触发器

    触发器:是一个修改指定数据时执行的存储过程. 创建触发器 Create Trigger trigger_name ON {table|view} { } 例子: insert触发器: create T ...

  5. EIP权限工作流平台总结-3后端框架

    1.预览地址:www.eipflow.com (1) 权限工作流:www.demo.eipflow.com/Account/Login (2) 基础权限版:www.auth.eipflow.com/A ...

  6. 剑指Spring源码(三)俯瞰Spring的Bean的生命周期(大众版)

    距离上一次写Spring源码解析,已经过去了快要好几个月了,主要原因还是Spring的源码解析类文章太难写了,不像我先前写的什么CAS源码,AQS源码,LinkedBlockingQueue等等,这些 ...

  7. UPC11073(DP,思维)

    #include<bits/stdc++.h>using namespace std;long long dp[507][507];const long long mod = 998244 ...

  8. Node.js 内置模块crypto加密模块(4) Diffie Hellman

    Diffie-Hellman( DH ):密钥交换协议/算法 ( Diffie-Hellman Key Exchange/Agreement Algorithm ) 百科摘录: Diffie-Hell ...

  9. 浏览器Quirksmode(怪异模式)与标准模式

    由于历史的原因,各个浏览器在对页面的渲染上存在差异,甚至同一浏览器在不同版本中,对页面的渲染也不同.在W3C标准出台以前,浏览器在对页面的渲染上没有统一规范,产生了差异(Quirks mode或者称为 ...

  10. Python Day22

    Django之Form组件 Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容 1.创建Form类 ...