Android-网络图片下载工具类-三级缓存

一、前言:   

之前写过一个简单的网络图片读取工具,读取网络图片,在Android应用中经常可见,在ListView、GridView这些控件中尤其可见。而为了提高用户体验,这些控件图片的读取,一般都是采用异步加载的方式,而使用缓存则是必不可少的环节。   现在网络上已经出现了很多功能丰富、使用简单的图片下载框架,例如universalimageloader,使用只需要初始化并且传入参数即可。这里我打算自己实现一个功能简单的图片下载工具类,采取了内存缓存+磁盘缓存+网络获取的三级缓存方式。


二、思路:     

        三级缓存,在网上查阅过很多资料,个人所知,有两种实现方式:

        1、软引用+SD卡缓存+网络获取

        2、Lrucache+DiskLruCache+网络获取

        因为从 android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。所以本文的思路是:先从内存缓存中寻找需要的图片,如果内存中不存在该图片,再从磁盘缓存中查找,如果再查找为空,则开启网络获取。


三、相关技术:

        这里主要使用第二种方式实现:

        1、关于Lrucache:

          Lrucache是Android自带的一个内存缓存类,专门用来做图片缓存处理的。它有一个特点,当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉。

       详细使用方法请自行百度。

       2、关于DiskLruCache:

       DiskLruCache是一个非Google官方编写,但获得官方认证的缓存类。它的作用是把图片(或者其他对象)保存到本地磁盘中,以便下次需要的时候,直接获取。

     DiskLruCache并没有限制数据的缓存位置,可以自由地进行设定,但是通常情况下多数应用程序都会将缓存的位置选择为 /sdcard/Android/data/<application package>/cache 这个路径。选择在这个位置有两点好处:第一,这是存储在SD卡上的,因此即使缓存再多的数据也不会对手机的内置存储空间有任何影响,只要SD卡空间足够就行。第二,这个路径被Android系统认定为应用程序的缓存路径,当程序被卸载的时候,这里的数据也会一起被清除掉,这样就不会出现删除程序之后手机上还有很多残留数据的问题。

    由于DiskLruCache并不是由Google官方编写的,所以这个类并没有被包含在Android API当中,我们需要将这个类从网上下载下来,然后手动添加到项目当中。DiskLruCache的源码在Google Source上,地址:android.googlesource.com/platform/libcore/+/jb-mr2-release/luni/src/main/java/libcore/io/DiskLruCache.java

    详细使用方法自行百度。


四、ImageLoad实现类:

  1. package com.zero.imageload;  
  2.   
  3. import android.content.Context;  
  4. import android.content.pm.PackageInfo;  
  5. import android.content.pm.PackageManager;  
  6. import android.graphics.Bitmap;  
  7. import android.graphics.BitmapFactory;  
  8. import android.os.AsyncTask;  
  9. import android.os.Environment;  
  10. import android.util.Log;  
  11. import android.util.LruCache;  
  12.   
  13. import java.io.BufferedInputStream;  
  14. import java.io.BufferedOutputStream;  
  15. import java.io.File;  
  16. import java.io.FileDescriptor;  
  17. import java.io.FileInputStream;  
  18. import java.io.IOException;  
  19. import java.io.OutputStream;  
  20. import java.net.HttpURLConnection;  
  21. import java.net.URL;  
  22. import java.security.MessageDigest;  
  23. import java.security.NoSuchAlgorithmException;  
  24. import java.util.HashSet;  
  25. import java.util.Set;  
  26.   
  27. /** 
  28.  * @author Zero 
  29.  * @version 1.0 
  30.  * @date 2015/7/29 
  31.  */  
  32. public class ImageLoad {  
  33.   
  34.   
  35.     private static ImageLoad mImageLoad = null;  
  36.   
  37.     private static String CacheName = “image”;    //图片缓存文件名  
  38.       
  39.       
  40.     /** 
  41.      * 获取唯一实例 
  42.      * @return 
  43.      */  
  44.     public static ImageLoad getInstance(Context context) {  
  45.         if (mImageLoad == null) {  
  46.             mImageLoad = new ImageLoad(context.getApplicationContext());  
  47.         }  
  48.         return mImageLoad;  
  49.     }  
  50.   
  51.       
  52.     /** 
  53.      * ImageLoad停止所有的任务 
  54.      * 通常在退出活动时,用于取消下载任务 
  55.      */  
  56.     public void ImageLoadCancel(){  
  57.         cancelAllTasks();  
  58.     }  
  59.   
  60.     /** 
  61.      * ImageLoad输出到日志 
  62.      * 通常到活动暂停时,用于刷新cache 
  63.      */  
  64.     public void ImageLoadPause(){  
  65.         fluchCache();  
  66.     }  
  67.   
  68.   
  69.     /** 
  70.      * ImageLoad获取磁盘缓存的大小 
  71.      */  
  72.     public long ImageLoadCacheSize(){  
  73.         return mDiskLruCache.size();  
  74.     }  
  75.   
  76.     /** 
  77.      * ImageLoad清理内存缓存 
  78.      */  
  79.     public void ImageLoadCacheClean(){  
  80.         //清理内存缓存  
  81.         clearCache();  
  82.     }  
  83.   
  84.     /** 
  85.      * ImageLoad磁盘缓存清理 
  86.      */  
  87.     public void ImageLoadDiskCacheClean(){  
  88.         try {  
  89.             mDiskLruCache.delete();  
  90.         } catch (IOException e) {  
  91.             e.printStackTrace();  
  92.         }  
  93.         System.gc();  
  94.     }  
  95.       
  96.     /** 
  97.      * ImageLoad清理所有的数据 
  98.      */  
  99.     public void ImageLoadCacheAllClean(){  
  100.         //清理内存缓存  
  101.         clearCache();  
  102.         //清理磁盘缓存  
  103.         try {  
  104.             mDiskLruCache.delete();  
  105.         } catch (IOException e) {  
  106.             e.printStackTrace();  
  107.         }  
  108.         System.gc();  
  109.     }  
  110.       
  111.       
  112.     /** 
  113.      * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象, 
  114.      * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。 
  115.      * @param imageUrl 
  116.      * @param bitmapListener 
  117.      */  
  118.     public void loadBitmap(String imageUrl,BitmapListener bitmapListener) {  
  119.   
  120.         //开启一个新的异步任务    
  121.         BitmapWorkerTask task = new BitmapWorkerTask(bitmapListener);  
  122.         //把任务加进任务集合  
  123.         taskCollection.add(task);  
  124.         //任务开始  
  125.         task.execute(imageUrl);  
  126.     }  
  127.   
  128.     /** 
  129.      * 加载Bitmap对象 
  130.      * @param imageUrl 
  131.      * @param width 
  132.      * @param height 
  133.      * @param bitmapListener 
  134.      */  
  135.     public void loadBitmap(String imageUrl,int width,int height,BitmapListener bitmapListener){  
  136.           
  137.     }  
  138.   
  139.     /** 
  140.      * 加载Bitmap对象,由于图片占用内存过大,在这里直接跳过内存缓存,存到磁盘缓存 
  141.      * @param imageUrl 
  142.      * @param bitmapListener 
  143.      */  
  144.     public void loadBigBitmap(String imageUrl,BitmapListener bitmapListener){  
  145.           
  146.     }  
  147.       
  148.   
  149.     /** 
  150.      * 记录所有正在下载或等待下载的任务。 
  151.      */  
  152.     private Set<BitmapWorkerTask> taskCollection;  
  153.       
  154.     /** 
  155.      * 为每个任务定义一个接口 
  156.      */  
  157.     public interface BitmapListener{  
  158.         public void onFailure();  
  159.         public void onSuccess(Bitmap bitmap);  
  160.     }  
  161.   
  162.       
  163.     /** 
  164.      * 异步下载图片的任务。 
  165.      * 
  166.      * @author zero 
  167.      */  
  168.     class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {  
  169.           
  170.         /** 
  171.          * 图片的URL地址 
  172.          */  
  173.         private String imageUrl;  
  174.   
  175.         /** 
  176.          * 绑定监听器 
  177.          */  
  178.         private BitmapListener mBitmapListener;  
  179.   
  180.         /** 
  181.          * 构造方法 
  182.          * @param bitmapListener 
  183.          */  
  184.         public BitmapWorkerTask(BitmapListener bitmapListener){  
  185.             this.mBitmapListener = bitmapListener;  
  186.         }  
  187.           
  188.         /** 
  189.          * 异步类执行 
  190.          * @param params 
  191.          * @return 
  192.          */  
  193.         @Override  
  194.         protected Bitmap doInBackground(String… params) {  
  195.             //获取URL  
  196.             imageUrl = params[0];  
  197.             Bitmap bitmap;  
  198.               
  199.             //先从缓存中获取  
  200.             bitmap = mMemoryCache.get(imageUrl);  
  201.             if(bitmap != null)  
  202.                 return bitmap;  
  203.               
  204.             //再从磁盘缓存中获取  
  205.             FileDescriptor fileDescriptor = null;  
  206.             FileInputStream fileInputStream = null;  
  207.             DiskLruCache.Snapshot snapShot = null;  
  208.             try {  
  209.                 // 生成图片URL对应的key  
  210.                 final String key = hashKeyForDisk(imageUrl);  
  211.                 // 查找key对应的缓存  
  212.                 snapShot = mDiskLruCache.get(key);  
  213.                 if (snapShot == null) {  
  214.                     // 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存  
  215.                     DiskLruCache.Editor editor = mDiskLruCache.edit(key);  
  216.                     if (editor != null) {  
  217.                         OutputStream outputStream = editor.newOutputStream(0);  
  218.                         if (downloadUrlToStream(imageUrl, outputStream)) {  
  219.                             editor.commit();  
  220.                         } else {  
  221.                             editor.abort();  
  222.                         }  
  223.                     }  
  224.                     // 缓存被写入后,再次查找key对应的缓存  
  225.                     snapShot = mDiskLruCache.get(key);  
  226.                 }  
  227.                 if (snapShot != null) {  
  228.                     fileInputStream = (FileInputStream) snapShot.getInputStream(0);  
  229.                     fileDescriptor = fileInputStream.getFD();  
  230.                 }  
  231.                 if (fileDescriptor != null) {  
  232.                     bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);  
  233.                 }  
  234.                 if (bitmap != null) {  
  235.                     // 将Bitmap对象添加到内存缓存当中  
  236.                     addBitmapToMemoryCache(params[0], bitmap);  
  237.                 }  
  238.                 return bitmap;  
  239.                   
  240.             } catch (IOException e) {  
  241.                 e.printStackTrace();  
  242.             } finally {  
  243.                 if (fileDescriptor == null && fileInputStream != null) {  
  244.                     try {  
  245.                         fileInputStream.close();  
  246.                     } catch (IOException e) {  
  247.                     }  
  248.                 }  
  249.             }  
  250.             return null;  
  251.         }  
  252.   
  253.         /** 
  254.          * 异步类执行返回 
  255.          * @param bitmap 
  256.          */  
  257.         @Override  
  258.         protected void onPostExecute(Bitmap bitmap) {  
  259.             super.onPostExecute(bitmap);  
  260.               
  261.             if(bitmap == null)  
  262.                 mBitmapListener.onFailure();  
  263.             else mBitmapListener.onSuccess(bitmap);  
  264.               
  265.             //移除任务  
  266.             taskCollection.remove(this);  
  267.         }  
  268.     }  
  269.   
  270.     /** 
  271.      * 取消所有正在下载或等待下载的任务。 
  272.      */  
  273.     public void cancelAllTasks() {  
  274.         if (taskCollection != null) {  
  275.             for (BitmapWorkerTask task : taskCollection) {  
  276.                 task.cancel(false);  
  277.             }  
  278.         }  
  279.     }  
  280.   
  281.     /** 
  282.      * 构造方法,由于是单例,因此构造方法为私有 
  283.      */  
  284.     private ImageLoad(Context context) {  
  285.   
  286.         // 初始化任务列表  
  287.         taskCollection = new HashSet<BitmapWorkerTask>();  
  288.           
  289.         // 初始化内存缓存类  
  290.         // 获取应用程序最大可用内存  
  291.         int maxMemory = (int) Runtime.getRuntime().maxMemory();  
  292.         int cacheSize = maxMemory / 8;  
  293.         // 设置图片缓存大小为程序最大可用内存的1/8  
  294.         mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
  295.             @Override  
  296.             protected int sizeOf(String key, Bitmap bitmap) {  
  297.                 return bitmap.getByteCount();  
  298.             }  
  299.         };  
  300.         // 初始化磁盘缓存类  
  301.         try {  
  302.             // 获取图片缓存路径  
  303.             File cacheDir = getDiskCacheDir(context, CacheName);  
  304.             if (!cacheDir.exists()) {  
  305.                 cacheDir.mkdirs();  
  306.             }  
  307.             // 创建DiskLruCache实例,初始化缓存数据  
  308.             mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 110 * 1024 * 1024);  
  309.         } catch (IOException e) {  
  310.             e.printStackTrace();  
  311.         }  
  312.     }  
  313.   
  314.   
  315.     /** 
  316.      * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。 
  317.      */  
  318.     private LruCache<String, Bitmap> mMemoryCache;  
  319.   
  320.     /** 
  321.      * 将一张图片存储到LruCache中。 
  322.      * 
  323.      * @param key    LruCache的键,这里传入图片的URL地址。 
  324.      * @param bitmap LruCache的键,这里传入从网络上下载的Bitmap对象。 
  325.      */  
  326.     public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
  327.         if (getBitmapFromMemoryCache(key) == null) {  
  328.             mMemoryCache.put(key, bitmap);  
  329.         }  
  330.     }  
  331.   
  332.     /** 
  333.      * 从LruCache中获取一张图片,如果不存在就返回null。 
  334.      * 
  335.      * @param key LruCache的键,这里传入图片的URL地址。 
  336.      * @return 对应传入键的Bitmap对象,或者null。 
  337.      */  
  338.     public Bitmap getBitmapFromMemoryCache(String key) {  
  339.         return mMemoryCache.get(key);  
  340.     }  
  341.   
  342.   
  343.     /** 
  344.      * 删除bitmap 
  345.      * 
  346.      * @param key 
  347.      */  
  348.     public void deleteBitmapFromMemoryCache(String key) {  
  349.         mMemoryCache.remove(key);  
  350.     }  
  351.   
  352.   
  353.     /** 
  354.      * 清空内存缓存 
  355.      * 
  356.      * @return void 
  357.      */  
  358.     public void clearCache() {  
  359.         if (mMemoryCache != null) {  
  360.             if (mMemoryCache.size() > 0) {  
  361.                 Log.d(”CacheUtils”“mMemoryCache.size() ” + mMemoryCache.size());  
  362.                 mMemoryCache.evictAll();  
  363.                 Log.d(”CacheUtils”“mMemoryCache.size()” + mMemoryCache.size());  
  364.             }  
  365.             // 初始化内存缓存类  
  366.             // 获取应用程序最大可用内存  
  367.             int maxMemory = (int) Runtime.getRuntime().maxMemory();  
  368.             int cacheSize = maxMemory / 8;  
  369.             // 设置图片缓存大小为程序最大可用内存的1/8  
  370.             mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
  371.                 @Override  
  372.                 protected int sizeOf(String key, Bitmap bitmap) {  
  373.                     return bitmap.getByteCount();  
  374.                 }  
  375.             };  
  376.         }  
  377.     }  
  378.   
  379.     /** 
  380.      * 图片硬盘缓存核心类, 用于缓存所有下载好的图片到本地 
  381.      */  
  382.     private DiskLruCache mDiskLruCache;  
  383.   
  384.     /** 
  385.      * 获取当前应用程序的版本号。 
  386.      */  
  387.     public int getAppVersion(Context context) {  
  388.         try {  
  389.             PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);  
  390.             return info.versionCode;  
  391.         } catch (PackageManager.NameNotFoundException e) {  
  392.             e.printStackTrace();  
  393.         }  
  394.         return 1;  
  395.     }  
  396.   
  397.     /** 
  398.      * 根据传入的uniqueName获取硬盘缓存的路径地址。 
  399.      */  
  400.     public File getDiskCacheDir(Context context, String uniqueName) {  
  401.         String cachePath;  
  402.         if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {  
  403.             cachePath = context.getExternalCacheDir().getPath();  
  404.         } else {  
  405.             cachePath = context.getCacheDir().getPath();  
  406.         }  
  407.         return new File(cachePath + File.separator + uniqueName);  
  408.     }  
  409.   
  410.     /** 
  411.      * 使用MD5算法对传入的key进行加密并返回。 
  412.      */  
  413.     public String hashKeyForDisk(String key) {  
  414.         String cacheKey;  
  415.         try {  
  416.             final MessageDigest mDigest = MessageDigest.getInstance(“MD5”);  
  417.             mDigest.update(key.getBytes());  
  418.             cacheKey = bytesToHexString(mDigest.digest());  
  419.         } catch (NoSuchAlgorithmException e) {  
  420.             cacheKey = String.valueOf(key.hashCode());  
  421.         }  
  422.         return cacheKey;  
  423.     }  
  424.   
  425.     private String bytesToHexString(byte[] bytes) {  
  426.         StringBuilder sb = new StringBuilder();  
  427.         for (int i = 0; i < bytes.length; i++) {  
  428.             String hex = Integer.toHexString(0xFF & bytes[i]);  
  429.             if (hex.length() == 1) {  
  430.                 sb.append(’0’);  
  431.             }  
  432.             sb.append(hex);  
  433.         }  
  434.         return sb.toString();  
  435.     }  
  436.   
  437.     /** 
  438.      * 将缓存记录同步到journal文件中。 
  439.      */  
  440.     public void fluchCache() {  
  441.         if (mDiskLruCache != null) {  
  442.             try {  
  443.                 mDiskLruCache.flush();  
  444.             } catch (IOException e) {  
  445.                 e.printStackTrace();  
  446.             }  
  447.         }  
  448.     }  
  449.   
  450.     /** 
  451.      * 建立HTTP请求,并获取Bitmap对象。 
  452.      * 
  453.      * @param urlString 图片的URL地址 
  454.      * @return 解析后的Bitmap对象 
  455.      */  
  456.     private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {  
  457.         HttpURLConnection urlConnection = null;  
  458.         BufferedOutputStream out = null;  
  459.         BufferedInputStream in = null;  
  460.         try {  
  461.             final URL url = new URL(urlString);  
  462.             urlConnection = (HttpURLConnection) url.openConnection();  
  463.             in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);  
  464.             out = new BufferedOutputStream(outputStream, 8 * 1024);  
  465.             int b;  
  466.             while ((b = in.read()) != -1) {  
  467.                 out.write(b);  
  468.             }  
  469.             return true;  
  470.         } catch (final IOException e) {  
  471.             e.printStackTrace();  
  472.         } finally {  
  473.             if (urlConnection != null) {  
  474.                 urlConnection.disconnect();  
  475.             }  
  476.             try {  
  477.                 if (out != null) {  
  478.                     out.close();  
  479.                 }  
  480.                 if (in != null) {  
  481.                     in.close();  
  482.                 }  
  483.             } catch (final IOException e) {  
  484.                 e.printStackTrace();  
  485.             }  
  486.         }  
  487.         return false;  
  488.     }  
  489. }  
package com.zero.imageload;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import android.util.LruCache;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.Set;

/**
 * @author Zero
 * @version 1.0
 * @date 2015/7/29
 */
public class ImageLoad {


    private static ImageLoad mImageLoad = null;

    private static String CacheName = "image";    //图片缓存文件名


    /**
     * 获取唯一实例
     * @return
     */
    public static ImageLoad getInstance(Context context) {
        if (mImageLoad == null) {
            mImageLoad = new ImageLoad(context.getApplicationContext());
        }
        return mImageLoad;
    }


    /**
     * ImageLoad停止所有的任务
     * 通常在退出活动时,用于取消下载任务
     */
    public void ImageLoadCancel(){
        cancelAllTasks();
    }

    /**
     * ImageLoad输出到日志
     * 通常到活动暂停时,用于刷新cache
     */
    public void ImageLoadPause(){
        fluchCache();
    }


    /**
     * ImageLoad获取磁盘缓存的大小
     */
    public long ImageLoadCacheSize(){
        return mDiskLruCache.size();
    }

    /**
     * ImageLoad清理内存缓存
     */
    public void ImageLoadCacheClean(){
        //清理内存缓存
        clearCache();
    }

    /**
     * ImageLoad磁盘缓存清理
     */
    public void ImageLoadDiskCacheClean(){
        try {
            mDiskLruCache.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.gc();
    }

    /**
     * ImageLoad清理所有的数据
     */
    public void ImageLoadCacheAllClean(){
        //清理内存缓存
        clearCache();
        //清理磁盘缓存
        try {
            mDiskLruCache.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.gc();
    }


    /**
     * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象,
     * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。
     * @param imageUrl
     * @param bitmapListener
     */
    public void loadBitmap(String imageUrl,BitmapListener bitmapListener) {

        //开启一个新的异步任务  
        BitmapWorkerTask task = new BitmapWorkerTask(bitmapListener);
        //把任务加进任务集合
        taskCollection.add(task);
        //任务开始
        task.execute(imageUrl);
    }

    /**
     * 加载Bitmap对象
     * @param imageUrl
     * @param width
     * @param height
     * @param bitmapListener
     */
    public void loadBitmap(String imageUrl,int width,int height,BitmapListener bitmapListener){

    }

    /**
     * 加载Bitmap对象,由于图片占用内存过大,在这里直接跳过内存缓存,存到磁盘缓存
     * @param imageUrl
     * @param bitmapListener
     */
    public void loadBigBitmap(String imageUrl,BitmapListener bitmapListener){

    }


    /**
     * 记录所有正在下载或等待下载的任务。
     */
    private Set<BitmapWorkerTask> taskCollection;

    /**
     * 为每个任务定义一个接口
     */
    public interface BitmapListener{
        public void onFailure();
        public void onSuccess(Bitmap bitmap);
    }


    /**
     * 异步下载图片的任务。
     *
     * @author zero
     */
    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {

        /**
         * 图片的URL地址
         */
        private String imageUrl;

        /**
         * 绑定监听器
         */
        private BitmapListener mBitmapListener;

        /**
         * 构造方法
         * @param bitmapListener
         */
        public BitmapWorkerTask(BitmapListener bitmapListener){
            this.mBitmapListener = bitmapListener;
        }

        /**
         * 异步类执行
         * @param params
         * @return
         */
        @Override
        protected Bitmap doInBackground(String... params) {
            //获取URL
            imageUrl = params[0];
            Bitmap bitmap;

            //先从缓存中获取
            bitmap = mMemoryCache.get(imageUrl);
            if(bitmap != null)
                return bitmap;

            //再从磁盘缓存中获取
            FileDescriptor fileDescriptor = null;
            FileInputStream fileInputStream = null;
            DiskLruCache.Snapshot snapShot = null;
            try {
                // 生成图片URL对应的key
                final String key = hashKeyForDisk(imageUrl);
                // 查找key对应的缓存
                snapShot = mDiskLruCache.get(key);
                if (snapShot == null) {
                    // 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存
                    DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                    if (editor != null) {
                        OutputStream outputStream = editor.newOutputStream(0);
                        if (downloadUrlToStream(imageUrl, outputStream)) {
                            editor.commit();
                        } else {
                            editor.abort();
                        }
                    }
                    // 缓存被写入后,再次查找key对应的缓存
                    snapShot = mDiskLruCache.get(key);
                }
                if (snapShot != null) {
                    fileInputStream = (FileInputStream) snapShot.getInputStream(0);
                    fileDescriptor = fileInputStream.getFD();
                }
                if (fileDescriptor != null) {
                    bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
                }
                if (bitmap != null) {
                    // 将Bitmap对象添加到内存缓存当中
                    addBitmapToMemoryCache(params[0], bitmap);
                }
                return bitmap;

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fileDescriptor == null && fileInputStream != null) {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                    }
                }
            }
            return null;
        }

        /**
         * 异步类执行返回
         * @param bitmap
         */
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);

            if(bitmap == null)
                mBitmapListener.onFailure();
            else mBitmapListener.onSuccess(bitmap);

            //移除任务
            taskCollection.remove(this);
        }
    }

    /**
     * 取消所有正在下载或等待下载的任务。
     */
    public void cancelAllTasks() {
        if (taskCollection != null) {
            for (BitmapWorkerTask task : taskCollection) {
                task.cancel(false);
            }
        }
    }

    /**
     * 构造方法,由于是单例,因此构造方法为私有
     */
    private ImageLoad(Context context) {

        // 初始化任务列表
        taskCollection = new HashSet<BitmapWorkerTask>();

        // 初始化内存缓存类
        // 获取应用程序最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;
        // 设置图片缓存大小为程序最大可用内存的1/8
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
        // 初始化磁盘缓存类
        try {
            // 获取图片缓存路径
            File cacheDir = getDiskCacheDir(context, CacheName);
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
            // 创建DiskLruCache实例,初始化缓存数据
            mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
     */
    private LruCache<String, Bitmap> mMemoryCache;

    /**
     * 将一张图片存储到LruCache中。
     *
     * @param key    LruCache的键,这里传入图片的URL地址。
     * @param bitmap LruCache的键,这里传入从网络上下载的Bitmap对象。
     */
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemoryCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    /**
     * 从LruCache中获取一张图片,如果不存在就返回null。
     *
     * @param key LruCache的键,这里传入图片的URL地址。
     * @return 对应传入键的Bitmap对象,或者null。
     */
    public Bitmap getBitmapFromMemoryCache(String key) {
        return mMemoryCache.get(key);
    }


    /**
     * 删除bitmap
     *
     * @param key
     */
    public void deleteBitmapFromMemoryCache(String key) {
        mMemoryCache.remove(key);
    }


    /**
     * 清空内存缓存
     *
     * @return void
     */
    public void clearCache() {
        if (mMemoryCache != null) {
            if (mMemoryCache.size() > 0) {
                Log.d("CacheUtils", "mMemoryCache.size() " + mMemoryCache.size());
                mMemoryCache.evictAll();
                Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
            }
            // 初始化内存缓存类
            // 获取应用程序最大可用内存
            int maxMemory = (int) Runtime.getRuntime().maxMemory();
            int cacheSize = maxMemory / 8;
            // 设置图片缓存大小为程序最大可用内存的1/8
            mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
                @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    return bitmap.getByteCount();
                }
            };
        }
    }

    /**
     * 图片硬盘缓存核心类, 用于缓存所有下载好的图片到本地
     */
    private DiskLruCache mDiskLruCache;

    /**
     * 获取当前应用程序的版本号。
     */
    public int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }

    /**
     * 根据传入的uniqueName获取硬盘缓存的路径地址。
     */
    public File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }

    /**
     * 使用MD5算法对传入的key进行加密并返回。
     */
    public String hashKeyForDisk(String key) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }

    private String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    /**
     * 将缓存记录同步到journal文件中。
     */
    public void fluchCache() {
        if (mDiskLruCache != null) {
            try {
                mDiskLruCache.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 建立HTTP请求,并获取Bitmap对象。
     *
     * @param urlString 图片的URL地址
     * @return 解析后的Bitmap对象
     */
    private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
        HttpURLConnection urlConnection = null;
        BufferedOutputStream out = null;
        BufferedInputStream in = null;
        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
            out = new BufferedOutputStream(outputStream, 8 * 1024);
            int b;
            while ((b = in.read()) != -1) {
                out.write(b);
            }
            return true;
        } catch (final IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }
}
五、使用方法:
  1. ImageLoad.getInstance(context).loadBitmap(imageurl, new BitmapListener() {  
  2.                   
  3.                 @Override  
  4.                 public void onSuccess(Bitmap bitmap) {  
  5.                     // TODO 自动生成的方法存根  
  6.                     ImageView image = (ImageView) v.findViewById(R.id.image);  
  7.                     image.setImageBitmap(bitmap);  
  8.                 }  
  9.                   
  10.                 @Override  
  11.                 public void onFailure() {  
  12.                     // TODO 自动生成的方法存根  
  13.                       
  14.                 }  
  15.             });  
ImageLoad.getInstance(context).loadBitmap(imageurl, new BitmapListener() {

                @Override
                public void onSuccess(Bitmap bitmap) {
                    // TODO 自动生成的方法存根
                    ImageView image = (ImageView) v.findViewById(R.id.image);
                    image.setImageBitmap(bitmap);
                }

                @Override
                public void onFailure() {
                    // TODO 自动生成的方法存根

                }
            });

六、不足之处

这个工具类目前只提供了一个图片下载方法,而且尚未做一些防止OOM发生的操作,尚有很多提高的地方,希望大家给些意见,大家一起互相交流,请狠狠得拍我。 - - 哈哈。



转载自: http://blog.csdn.net/z82367825/article/details/46509235

  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值