当前位置:   article > 正文

笔记---ImageLoader实现图片压缩,缓存_imageloader 压缩图片

imageloader 压缩图片
    以下内容为博主阅读《Android开发艺术探索》所记代码以及知识点的梳理
  1. package com.example.utils;
  2. import java.io.BufferedInputStream;
  3. import java.io.BufferedOutputStream;
  4. import java.io.File;
  5. import java.io.FileDescriptor;
  6. import java.io.FileInputStream;
  7. import java.io.IOException;
  8. import java.io.OutputStream;
  9. import java.net.HttpURLConnection;
  10. import java.net.URL;
  11. import java.security.MessageDigest;
  12. import java.security.NoSuchAlgorithmException;
  13. import java.util.concurrent.Executor;
  14. import java.util.concurrent.LinkedBlockingDeque;
  15. import java.util.concurrent.ThreadFactory;
  16. import java.util.concurrent.ThreadPoolExecutor;
  17. import java.util.concurrent.TimeUnit;
  18. import java.util.concurrent.atomic.AtomicInteger;
  19. import android.content.Context;
  20. import android.graphics.Bitmap;
  21. import android.graphics.BitmapFactory;
  22. import android.os.Build;
  23. import android.os.Handler;
  24. import android.os.Looper;
  25. import android.os.Message;
  26. import android.os.StatFs;
  27. import android.os.Build.VERSION_CODES;
  28. import android.os.Environment;
  29. import android.support.v4.util.LruCache;
  30. import android.util.Log;
  31. import android.widget.ImageView;
  32. public class ImageLoader {
  33. private static final String TAG = "ImageLoader";
  34. public static final int MESSAGE_POST_RESULT = 1;
  35. private static final int CPU_COUNT = Runtime.getRuntime()
  36. .availableProcessors();
  37. private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
  38. private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
  39. private static final long KEEP_ALIVE = 10L;
  40. private static final int IO_BUFFER_SIZE = 8 * 1024;
  41. private static final int DISK_CACHE_INDEX = 0;
  42. private boolean mIsDiskLruCacheCreated = false;
  43. private Context mContext;
  44. private DiskLruCache mDiskLruCache;
  45. private static final int TAG_KEY_URI = 100;
  46. private ImageResizer mImageResizer = new ImageResizer();
  47. private LruCache<String, Bitmap> mMemoryCache;
  48. private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;
  49. // 创建线程工厂,使用原子类保证线程安全
  50. private static final ThreadFactory sThreadFactory = new ThreadFactory() {
  51. // 内部被volatile修饰,线程安全
  52. private final AtomicInteger mCount = new AtomicInteger(1);
  53. @Override
  54. public Thread newThread(Runnable r) {
  55. // 执行任务r,线程名"ImageLoader#"+mCount.getAndIncrement()
  56. return new Thread(r, "ImageLoader#" + mCount.getAndIncrement());
  57. }
  58. };
  59. // 线程池(核心线程,最大线程,待机时间单位,工作队列,线程工厂)
  60. public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
  61. CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,
  62. new LinkedBlockingDeque<Runnable>(), sThreadFactory);
  63. private Handler mMainHandler = new Handler(Looper.getMainLooper()) {
  64. @Override
  65. public void handleMessage(Message msg) {
  66. LoaderResult result = (LoaderResult) msg.obj;
  67. ImageView imageView = result.imageView;
  68. // imageView.setImageBitmap(result.bitmap);(书中多了此段代码,估计为纰漏)
  69. String uri = (String) imageView.getTag(TAG_KEY_URI);
  70. //解决快速滑动时imageView复用,uri变化而使得图片错位
  71. if (uri.equals(result.uri)) {
  72. imageView.setImageBitmap(result.bitmap);
  73. } else {
  74. Log.w(TAG, "url is change,ignored!");
  75. }
  76. };
  77. };
  78. // 异步加载接口设计
  79. public void bindBitmap(final String uri, final ImageView imageView,
  80. final int reqWidth, final int reqHeight) {
  81. imageView.setTag(TAG_KEY_URI, uri);
  82. Bitmap bitmap = loadBitmapFromMemCache(uri);
  83. if (bitmap != null) {
  84. imageView.setImageBitmap(bitmap);
  85. return;
  86. }
  87. Runnable loadBitmapTask = new Runnable() {
  88. @Override
  89. public void run() {
  90. Bitmap bitmap = loadBitmap(uri, reqWidth, reqHeight);
  91. if (bitmap != null) {
  92. LoaderResult result = new LoaderResult(imageView, uri,
  93. bitmap);
  94. // obtainMessage().sendToTarget()和sendMessage()区别在于前者是从MessagePool拿的,后者是自己创建的
  95. mMainHandler.obtainMessage(MESSAGE_POST_RESULT, result)
  96. .sendToTarget();
  97. }
  98. }
  99. };
  100. THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
  101. }
  102. // 同步加载接口设计
  103. protected Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) {
  104. Bitmap bitmap = loadBitmapFromMemCache(uri);
  105. if (bitmap != null) {
  106. return bitmap;
  107. }
  108. bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);
  109. if (bitmap != null) {
  110. return bitmap;
  111. }
  112. try {
  113. bitmap = loadBitmapFromHttp(uri, reqWidth, reqWidth);
  114. } catch (IOException e) {
  115. e.printStackTrace();
  116. }
  117. if (bitmap == null && !mIsDiskLruCacheCreated) {
  118. bitmap = downloadBitmapFromUrl(uri);
  119. }
  120. return bitmap;
  121. }
  122. // 从网络加载bitmap数据
  123. private Bitmap downloadBitmapFromUrl(String uri) {
  124. Bitmap bitmap = null;
  125. HttpURLConnection urlConnection = null;
  126. BufferedInputStream is = null;
  127. try {
  128. URL url = new URL(uri);
  129. urlConnection = (HttpURLConnection) url.openConnection();
  130. is = new BufferedInputStream(urlConnection.getInputStream(),
  131. IO_BUFFER_SIZE);
  132. bitmap = BitmapFactory.decodeStream(is);
  133. } catch (IOException e) {
  134. e.printStackTrace();
  135. } finally {
  136. if (urlConnection != null) {
  137. urlConnection.disconnect();
  138. }
  139. IOutil.close(is);
  140. }
  141. return bitmap;
  142. }
  143. // 从网络加载bitmap数据,并将其写入到磁盘缓存
  144. private Bitmap loadBitmapFromHttp(String uri, int reqWidth, int reqHeight)
  145. throws IOException {
  146. if (Looper.myLooper() == Looper.getMainLooper()) {
  147. throw new RuntimeException("can not visit network from UI Thread.");
  148. }
  149. String key = hashKeyFormUrl(uri);
  150. // 磁盘缓存写入
  151. DiskLruCache.Editor editor = mDiskLruCache.edit(key);
  152. if (editor != null) {
  153. // 磁盘缓存输出流
  154. OutputStream os = editor.newOutputStream(DISK_CACHE_INDEX);
  155. // 写入成功则返回true
  156. if (downloadUrlToStream(uri, os)) {
  157. // 提交
  158. editor.commit();
  159. } else {
  160. // 撤销
  161. editor.abort();
  162. }
  163. mDiskLruCache.flush();
  164. }
  165. return loadBitmapFromDiskCache(uri, reqWidth, reqHeight);
  166. }
  167. private boolean downloadUrlToStream(String uri, OutputStream os) {
  168. HttpURLConnection urlConnection = null;
  169. BufferedOutputStream out = null;
  170. BufferedInputStream in = null;
  171. try {
  172. URL url = new URL(uri);
  173. urlConnection = (HttpURLConnection) url.openConnection();
  174. in = new BufferedInputStream(urlConnection.getInputStream());
  175. out = new BufferedOutputStream(os, IO_BUFFER_SIZE);
  176. int length = 0;
  177. while ((length = in.read()) != -1) {
  178. out.write(length);
  179. }
  180. return true;
  181. } catch (IOException e) {
  182. e.printStackTrace();
  183. } finally {
  184. if (urlConnection != null) {
  185. urlConnection.disconnect();
  186. }
  187. IOutil.close(out);
  188. IOutil.close(in);
  189. }
  190. return false;
  191. }
  192. // 加载磁盘缓存中的图片
  193. private Bitmap loadBitmapFromDiskCache(String uri, int reqWidth,
  194. int reqHeight) {
  195. if (Looper.getMainLooper() == Looper.myLooper()) {
  196. Log.w(TAG, "load bitmap from UI Thread,it's not recommended!");
  197. }
  198. if (mDiskLruCache == null) {
  199. return null;
  200. }
  201. Bitmap bitmap = null;
  202. String key = hashKeyFormUrl(uri);
  203. try {
  204. DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
  205. if (snapshot != null) {
  206. FileInputStream fis = (FileInputStream) snapshot
  207. .getInputStream(DISK_CACHE_INDEX);
  208. // 获取该文件输入流fis相关的文件描述对象fileDescriptor
  209. FileDescriptor fileDescriptor = fis.getFD();
  210. bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(
  211. fileDescriptor, reqWidth, reqHeight);
  212. if (bitmap != null) {
  213. //成功获取磁盘缓存的图片后将其添加到内存缓存
  214. addBitmapToMemoryCache(key, bitmap);
  215. }
  216. }
  217. } catch (IOException e) {
  218. e.printStackTrace();
  219. }
  220. return bitmap;
  221. }
  222. // 加载内存缓存中的图片
  223. private Bitmap loadBitmapFromMemCache(String uri) {
  224. final String key = hashKeyFormUrl(uri);
  225. Bitmap bitmap = getBitmapFromMemoryCache(key);
  226. return bitmap;
  227. }
  228. // 私有构造方法,初始化成员变量
  229. private ImageLoader(Context context) {
  230. mContext = context;
  231. int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
  232. int cacheMemory = maxMemory / 8;
  233. mMemoryCache = new LruCache<String, Bitmap>(cacheMemory) {
  234. @Override
  235. protected int sizeOf(String key, Bitmap value) {
  236. return value.getRowBytes() * value.getHeight() / 1024;
  237. }
  238. };
  239. File diskCacheDir = getDiskCacheDir(context, "bitmap");
  240. if (diskCacheDir.exists()) {
  241. diskCacheDir.mkdirs();
  242. }
  243. if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) {
  244. try {
  245. mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1,
  246. DISK_CACHE_SIZE);
  247. mIsDiskLruCacheCreated = true;
  248. } catch (IOException e) {
  249. e.printStackTrace();
  250. }
  251. }
  252. }
  253. // 往内存缓存中添加bitmap数据
  254. private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
  255. if (getBitmapFromMemoryCache(key) == null) {
  256. mMemoryCache.put(key, bitmap);
  257. }
  258. }
  259. // 从内存缓存中获取Bitmap对象
  260. private Bitmap getBitmapFromMemoryCache(String key) {
  261. return mMemoryCache.get(key);
  262. }
  263. // 返回ImageLoader对象
  264. public static ImageLoader build(Context context) {
  265. return new ImageLoader(context);
  266. }
  267. // 获取文件的可用空间
  268. private long getUsableSpace(File path) {
  269. // 判断SDK版本是否大于姜饼版本(2.3版本)
  270. if (Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
  271. return path.getUsableSpace();
  272. }
  273. // 低版本无法使用上述方法,防止报错
  274. final StatFs stats = new StatFs(path.getPath());
  275. return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();
  276. }
  277. // 获取磁盘缓存的路径
  278. private File getDiskCacheDir(Context context, String uniqueName) {
  279. // 判断SD卡是否存在并且没有被移除
  280. boolean externalStorageAvailable = Environment
  281. .getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
  282. final String cachePath;
  283. if (externalStorageAvailable) {
  284. cachePath = context.getExternalCacheDir().getPath();
  285. } else {
  286. cachePath = context.getCacheDir().getPath();
  287. }
  288. // 路径+“\”+文件名
  289. return new File(cachePath + File.separator + uniqueName);
  290. }
  291. private String bytesToHexString(byte[] bytes) {
  292. StringBuilder sb = new StringBuilder();
  293. for (int i = 0; i < bytes.length; i++) {
  294. // 将字节数组转换为字符数组,清除高位二十四比特(byte为8位,int为32位)
  295. String hex = Integer.toHexString(0xFF & bytes[i]);
  296. if (hex.length() == 1) {
  297. sb.append('0');
  298. }
  299. sb.append(hex);
  300. }
  301. return sb.toString();
  302. }
  303. // 给url地址进行MD5加密
  304. private String hashKeyFormUrl(String url) {
  305. String cacheKey;
  306. try {
  307. final MessageDigest mDigest = MessageDigest.getInstance("MD5");
  308. mDigest.update(url.getBytes());
  309. cacheKey = bytesToHexString(mDigest.digest());
  310. } catch (NoSuchAlgorithmException e) {
  311. cacheKey = String.valueOf(url.hashCode());
  312. }
  313. return cacheKey;
  314. }
  315. // 将imageView,url,bitmap封装成实体类,以便Handler传输数据
  316. private static class LoaderResult {
  317. public ImageView imageView;
  318. public String uri;
  319. public Bitmap bitmap;
  320. public LoaderResult(ImageView imageView, String uri, Bitmap bitmap) {
  321. this.imageView = imageView;
  322. this.uri = uri;
  323. this.bitmap = bitmap;
  324. }
  325. }
  326. }

实现功能:图片的同步加载、图片的异步加载、图片压缩、内存缓存、磁盘缓存、网络垃圾

详情见图片和代码注释



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/105867
推荐阅读
相关标签
  

闽ICP备14008679号