Android图片二级缓存

Posted yangykaifa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android图片二级缓存相关的知识,希望对你有一定的参考价值。

点击下载源代码

想起刚開始写代码的时候,领导叫我写一个头像下载的方法,当时屁颠屁颠就写了一个图片下载的,每次都要去网络上请求,最后直接被pass掉了

当时的思路是这种

技术分享

后来渐渐地就知道了有二级缓存这东西。

自己也阅读过非常多关于双缓存的文章。

APP开发到越后面。对性能的要求越高。那么双缓存的优势就逐渐体现出来了。

所谓图片双缓存。首先到执行内存中请求,再到sd卡请求,最后到网络请求,流程图例如以下

技术分享

那我们从第一部開始解析

1.先看 内存缓存的代码

[java] view plaincopy
  1. public class MemoryCache implements ImageCache {  
  2.     private static final String TAG = MemoryCache.class.getSimpleName();  
  3.     private LruCache<String,Bitmap> mMemoryCache;  
  4.     public MemoryCache(){  
  5.         init();  
  6.     }  
  7.   
  8.     private void init(){  
  9.         final int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);  
  10.         final int cacheSize = maxMemory/4;  
  11.         mMemoryCache = new LruCache<String,Bitmap>(cacheSize){  
  12.             @Override  
  13.             protected int sizeOf(String key, Bitmap value) {  
  14.                 return value.getRowBytes()*value.getHeight()/1024;  
  15.             }  
  16.         };  
  17.     }  
  18.     @Override  
  19.     public Bitmap get(String key) {  
  20.         Bitmap bitmap = mMemoryCache.get(key);  
  21.         if (bitmap!=null){  
  22.             Log.i(TAG,"File is exist in memory");  
  23.         }  
  24.         return mMemoryCache.get(key);  
  25.     }  
  26.   
  27.     @Override  
  28.     public void put(String key, Bitmap bitmap) {  
  29.         if (get(key)==null) {  
  30.             mMemoryCache.put(key, bitmap);  
  31.         }  
  32.     }  
  33. }  


[java] view plaincopy
  1. private void init()  
init()方法中对一些变量进行初始化,mMemoryCache用于在内存中缓存图片

[java] view plaincopy
  1. public Bitmap get(String key) {}  

get()方法用于从内存中获得缓存

[java] view plaincopy
  1. public void put(String key, Bitmap bitmap) {}  
put()方法将下载好的图片缓存到内存中,方便下次使用


2.再看sd卡缓存

[java] view plaincopy
  1. public class DiskCache implements ImageCache {  
  2.     private static final String TAG = DiskCache.class.getSimpleName();  
  3.     static String mPath ;  
  4.     public DiskCache(Context context){  
  5.         init(context);  
  6.     }  
  7.     private void init(Context context){  
  8.         // 获取图片缓存路径  
  9.         mPath = getDiskCachePath(context,"bitmap");  
  10.         File cacheDir = new File(mPath);  
  11.         if (!cacheDir.exists()) {  
  12.             cacheDir.mkdirs();  
  13.         }  
  14.     }  
  15.     @Override  
  16.     public Bitmap get(String key) {  
  17.         File file = new File(mPath+key);  
  18.         if (file.exists()){  
  19.             return BitmapFactory.decodeFile(mPath+key);  
  20.         }  
  21.         return null;  
  22.     }  
  23.   
  24.     @Override  
  25.     public void put(String key, Bitmap bitmap) {  
  26.         FileOutputStream fileOutputStream = null;  
  27.         try {  
  28.             File file = new File(mPath+key);  
  29.             if (file.exists()){  
  30.                 Log.i(TAG,"File is exist on disk");  
  31.             }  
  32.             fileOutputStream = new FileOutputStream(mPath+key);  
  33.             bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);  
  34.   
  35.         } catch (FileNotFoundException e) {  
  36.             e.printStackTrace();  
  37.         }finally {  
  38.             CloseUtils.closeQuietly(fileOutputStream);  
  39.         }  
  40.     }  
  41.   
  42.     /** 
  43.      * 依据传入的dir获得路径 
  44.      * @param context 
  45.      * @param dir 
  46.      * @return 
  47.      */  
  48.     public String getDiskCachePath(Context context, String dir) {  
  49.         String cachePath;  
  50.         if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())  
  51.                 || !Environment.isExternalStorageRemovable()) {  
  52.             cachePath = context.getExternalCacheDir().getPath();  
  53.         } else {  
  54.             cachePath = context.getCacheDir().getPath();  
  55.         }  
  56.         return cachePath + File.separator + dir;  
  57.     }  
  58.   
  59. }  
相同

[java] view plaincopy
  1. private void init()  
init()方法中对一些变量进行初始化,mMemoryCache用于在内存中缓存图片

[java] view plaincopy
  1. public Bitmap get(String key) {}  

get()方法用于从内存中获得缓存

[java] view plaincopy
  1. public void put(String key, Bitmap bitmap) {}  
put()方法将下载好的图片缓存到内存中,方便下次使用

接下来我们会在一个叫DoubleCache的类中对以上两种缓存方式进行管理

[java] view plaincopy
  1. public class DoubleCache implements ImageCache {  
  2.     private static final String TAG = DoubleCache.class.getSimpleName();  
  3.     private MemoryCache mMemoryCache = null;  
  4.     private DiskCache mDiskCache = null;  
  5.   
  6.     public DoubleCache(Context context){  
  7.         mMemoryCache = new MemoryCache();  
  8.         mDiskCache = new DiskCache(context);  
  9.     }  
  10.     @Override  
  11.     public Bitmap get(String url) {  
  12.         String key = url2Key(url);  
  13.         Bitmap bitmap = mMemoryCache.get(key);  
  14.         if(bitmap==null){  
  15.             bitmap = mDiskCache.get(key);  
  16.         }else {  
  17.         }  
  18.         return bitmap;  
  19.      }  
  20.   
  21.     @Override  
  22.     public void put(String url, Bitmap bitmap) {  
  23.         String key = url2Key(url);  
  24.         mMemoryCache.put(key,bitmap);  
  25.         mDiskCache.put(key,bitmap);  
  26.     }  
  27.   
  28.     //url转key  
  29.     private String url2Key(String url){  
  30.         String key = MD5.hashKeyForDisk(url)+".jpg";  
  31.         return key;  
  32.     }  
  33. }  
我们在获取缓存的时候先从内存中获取。当内存中击中直接返回,当内存中没有击中,则訪问sd卡。

3.看到这里,小伙伴们一定急了,这仅仅有从缓存中和sd卡中取图片,并没有从网络获取,别急。立即就来

[java] view plaincopy
  1. public class ImageLoader {  
  2.     private static final String TAG = ImageLoader.class.getSimpleName();  
  3.       
  4.     private static ImageLoader sInstance;  
  5.   
  6.     private DoubleCache mDoubleCache = null;  
  7.   
  8.     private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());  
  9.   
  10.     private ImageLoader(Context context) {  
  11.         mDoubleCache = new DoubleCache(context);  
  12.     }  
  13.   
  14.     public static ImageLoader getInstance(Context context) {  
  15.         if (sInstance == null) {  
  16.             synchronized (ImageLoader.class) {  
  17.                 sInstance = new ImageLoader(context);  
  18.             }  
  19.         }  
  20.         return sInstance;  
  21.     }  
  22.   
  23.     public void displayImage(String url, ImageView imageView) {  
  24.         Bitmap bitmap = mDoubleCache.get(url);  
  25.         if (bitmap != null) {  
  26.             imageView.setImageBitmap(bitmap);  
  27.             mDoubleCache.put(url,bitmap);  
  28.             return;  
  29.         }  
  30.         submitLoadRequest(url, imageView);  
  31.     }  
  32.   
  33.     private void submitLoadRequest(final String url, final ImageView imageView) {  
  34.         Log.i(TAG,"Download,url:"+url);  
  35.         imageView.setTag(url);  
  36.         mExecutorService.submit(new Runnable() {  
  37.             @Override  
  38.             public void run() {  
  39.                 final Bitmap bitmap = downloadImage(url);  
  40.                 if (imageView.getTag().equals(url)) {  
  41.   
  42.                     imageView.post(new Runnable() {  
  43.                         @Override  
  44.                         public void run() {  
  45.                             imageView.setImageBitmap(bitmap);  
  46.                         }  
  47.                     });  
  48.                 }  
  49.                 mDoubleCache.put(url, bitmap);  
  50.             }  
  51.         });  
  52.     }  
  53.     Handler handler = new Handler(){  
  54.         @Override  
  55.         public void handleMessage(Message msg) {  
  56.   
  57.         }  
  58.     };  
  59.   
  60.     public Bitmap downloadImage(String url) {  
  61.         Bitmap bitmap = null;  
  62.         HttpURLConnection conn = null;  
  63.         try {  
  64.             URL url1 = new URL(url);  
  65.             conn = (HttpURLConnection) url1.openConnection();  
  66.             bitmap = BitmapFactory.decodeStream(conn.getInputStream());  
  67.             if (bitmap!=null){  
  68.                 mDoubleCache.put(url,bitmap);  
  69.             }  
  70.         } catch (MalformedURLException e) {  
  71.             e.printStackTrace();  
  72.         } catch (Exception e) {  
  73.             e.printStackTrace();  
  74.         } finally {  
  75.             if (conn != null) {  
  76.                 conn.disconnect();  
  77.             }  
  78.         }  
  79.         return bitmap;  
  80.     }  

[java] view plaincopy
  1. displayImage()  
方法中能够看到。假设缓存中都没有才从网络中获取

[java] view plaincopy
  1. public Bitmap downloadImage(String url) {}  
下载完毕之后。把图片放到缓存中。

到这里,我们的第二张流程图就走完了。是不是非常easy。

我们在看一下是怎样使用的

[java] view plaincopy
  1. private ImageView imageView;  
  2. private ImageView imageView2;  
  3. @Override  
  4. protected void onCreate(Bundle savedInstanceState) {  
  5.     super.onCreate(savedInstanceState);  
  6.     setContentView(R.layout.activity_main);  
  7.     imageView = (ImageView) findViewById(R.id.image);  
  8.     imageView2 = (ImageView) findViewById(R.id.image2);  
  9.     imageView.setOnClickListener(new View.OnClickListener() {  
  10.         @Override  
  11.         public void onClick(View v) {  
  12.   
  13.             ImageLoader.getInstance(MainActivity.this).displayImage("http://img.my.csdn.net/uploads/201407/26/1406383299_1976.jpg", imageView);  
  14.         }  
  15.     });  
  16.     imageView2.setOnClickListener(new View.OnClickListener() {  
  17.         @Override  
  18.         public void onClick(View v) {  
  19.   
  20.             ImageLoader.getInstance(MainActivity.this).displayImage("http://img.my.csdn.net/uploads/201407/26/1406383299_1976.jpg", imageView2);  
  21.         }  
  22.     });  
  23. }  


点击下载源代码









以上是关于Android图片二级缓存的主要内容,如果未能解决你的问题,请参考以下文章

让App中增加LruCache缓存,轻松解决图片过多造成的OOM

Android中的缓存机制与实现

Android Picasso图片加载库源码剖析

Android Picasso图片加载库源码剖析

Android异步任务AsyncTask的使用与原理分析

Android:深入剖析图片加载库Glide缓存功能(源码分析)