本文主要是介绍16.手写图片加载框架ImageLoader,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
概述
第三方开源的图片框架很多,这里自己去写一个的目的是通过这样一个写的过程,拓展自己对架构设计的理解,包括设计模式,线程,策略,缓存等等。另外大型的框架例如Glide,代码很完善,扩展性很高,但是阅读起来有难度,而实际上,这些框架底层实现原理都是类似的,所以通过构建一个简单框架的过程更加有助于对其原理的理解,算是为阅读复杂的第三方源码打下一个基础。
github地址:https://github.com/renzhenming/ImageLoader.git
今天的框架要实现一下的功能:
1.根据用户需求可以灵活配置(建造者模式)
2.支持高并发,图片加载的优先级
3.支持可以选择不同的加载策略,对加载策略进行扩展
4.二级缓存 加载图片时内存中已经加载了,则从内存中加载,不存在去外置卡中5.加载,外置还不存在则从网络下载
6.并对缓存策略可以扩展
7.支持从加载过程中显示默认加载图片
8.支持加载失败时 显示默认错误图片
9.图片显示自适应。从网络加载下来的图片经最佳比例压缩后显示不能失真变形
10.支持请求转发,下载
用到的模式:
1.生产者 消费者模式
2.建造者模式
3.单例模式
4.模板方法模式
5.策略模式
用到的知识点
1.内存缓存 LruCache技术
2.硬盘缓存技术DiskLruCache技术
3.图片下载时请求转发
框架构建流程
如上图,首先使用Builder设计模式构建ImageLoaderConfig,这个类处理图片加载框架的全局配置信息,包括加载策略,缓存策略,线程数,以及加载中一些图片的配置,封装成了DisplayConfig对象;SimpleImageLoader是对外暴露的主类,它持有配置对象的引用,所以它可以调用所以图片加载中所涉及的配置,然后将这些配置封装成BitmapRequest对象,一个BitmapRequest对象对应一次网络请求,在SimpleImageLoader初始化的同时还会初始化全局的请求队列RequestQueue,BitmapRequest对象会被加入队列中,这时候分发器开始工作,将BitmapRequest按照一定协议分发给不同的加载器,加载器拿到请求后先从缓存BitmapCache中获取,有缓存则直接展示然后再去加载网络图片,加载网络图片的时候又涉及到加载策略的选择,根据不同策略进行不同的加载。
关键代码
1.配置管理
ImageLoaderConfig ,配置管理类,我们把一些关键的配置信息单独封装起来,以Builder模式构建,用户可以自由的配置自己需要的,同时提高扩展性,这正是Builder设计模式的优点.ImageLoaderConfig 负责处理整个框架的配置信息,目前包括缓存策略,加载策略,加载中显示的占位图,加载失败展示的占位图等等
package com.rzm.imageloader.config;import com.rzm.imageloader.cache.BitmapCache;
import com.rzm.imageloader.policy.LoadPolicy;/*** Author:renzhenming* Time:2018/6/13 7:21* Description:This is ImageLoaderConfig* 以建造者模式实现,管理ImageLoader配置信息*/
public class ImageLoaderConfig {/*** 图片显示配置 TODO 初始化*/private DisplayConfig displayConfig;/*** 缓存策略*/private BitmapCache bitmapCache;/*** 加载策略*/private LoadPolicy loadPolicy;/*** 默认线程数*/private int threadCount = Runtime.getRuntime().availableProcessors();private ImageLoaderConfig(){}/*** 建造者模式*/public static class Builder{/*** Builder持有外部类的引用,在new的时候创建出来*/private ImageLoaderConfig config;public Builder(){config = new ImageLoaderConfig();}/*** 设置缓存策略* @param bitmapCache* @return*/public Builder setCachePolicy(BitmapCache bitmapCache){config.bitmapCache = bitmapCache;return this;}/*** 设置加载策略* @param loadPolicy* @return*/public Builder setLoadPolicy(LoadPolicy loadPolicy){config.loadPolicy = loadPolicy;return this;}/*** 设置线程数* @param threadCount* @return*/public Builder setThreadCount(int threadCount){config.threadCount = threadCount;return this;}/*** 设置加载过程中的图片* @param resId* @return*/public Builder setLoadingImage(int resId){if (config.displayConfig == null){throw new NullPointerException("you have not set DisplayConfig,DisplayConfig is null");}config.displayConfig.loadingImage = resId;return this;}/*** 设置加载失败显示的图片* @param resId* @return*/public Builder setErrorImage(int resId){if (config.displayConfig == null){throw new NullPointerException("you have not set DisplayConfig,DisplayConfig is null");}config.displayConfig.errorImage = resId;return this;}/*** 构建* @return*/public ImageLoaderConfig build(){return config;}}public DisplayConfig getDisplayConfig() {return displayConfig;}public BitmapCache getBitmapCache() {return bitmapCache;}public LoadPolicy getLoadPolicy() {return loadPolicy;}public int getThreadCount() {return threadCount;}
}
package com.rzm.commonlibrary.general.imageloader.config;/*** Author:renzhenming* Time:2018/6/13 7:24* Description:This is DisplayConfig* 图片显示配置类,单独拿出来作为一个单独类有利于扩展,仿Glide*/
public class DisplayConfig {/*** 加载过程中的占位图片*/public int loadingImage = -1;/*** 加载失败显示的图片*/public int errorImage = -1;
}
2.SimpleImageLoader
以单例形式构建的交互类,持有ImageLoaderConfig 配置引用,负责将每一个网络请求封装成BitmapRequest对象,SimpleImageLoader初始化的时候会构建出一个全局的阻塞式队列,BitmapRequest会被加入这个队列中,此时分发器开始工作,分发器的构建启发于Android消息队列中的looper,负责将每个Request请求从队列中取出交给负责处理这个请求的加载器
package com.rzm.commonlibrary.general.imageloader.loader;import android.graphics.Bitmap;
import android.widget.ImageView;import com.rzm.commonlibrary.general.imageloader.config.DisplayConfig;
import com.rzm.commonlibrary.general.imageloader.config.ImageLoaderConfig;
import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;
import com.rzm.commonlibrary.general.imageloader.request.RequestQueue;/*** Author:renzhenming* Time:2018/6/13 7:22* Description:This is SimpleImageLoader*/
public class SimpleImageLoader {/*** 持有配置信息对象的引用*/private ImageLoaderConfig config;private static volatile SimpleImageLoader instance;/*** 请求队列*/private RequestQueue requestQueue;private SimpleImageLoader(){}private SimpleImageLoader(ImageLoaderConfig config){this.config = config;//初始化请求队列requestQueue = new RequestQueue(config.getDispatcherCount());//开启请求队列requestQueue.start();}/*** 用于在Application中初始化ImageLoader 设置配置信息* 必须,否则调用空参getInstance()会抛异常* @param config* @return*/public static SimpleImageLoader init(ImageLoaderConfig config){if (instance == null){synchronized (SimpleImageLoader.class){if (instance == null){instance = new SimpleImageLoader(config);}}}return instance;}/*** 用于在代码中获取单例对象* @return*/public static SimpleImageLoader getInstance(){if (instance == null){throw new UnsupportedOperationException("SimpleImageLoader haven't been init with ImageLoaderConfig,call init(ImageLoaderConfig config) in your application");}return instance;}/*** 显示图片* @param imageView* @param url*/public void display(ImageView imageView,String url){display(imageView,url,null,null);}/*** 显示图片* @param imageView* @param url*/public void display(ImageView imageView,String url,DisplayConfig displayConfig){display(imageView,url,displayConfig,null);}/*** 显示图片* @param imageView* @param url*/public void display(ImageView imageView,String url,ImageListener listener){display(imageView,url,null,listener);}/*** 显示图片,用于针对特殊图片配置特殊的配置信息* @param imageView* @param url* @param displayConfig* @param listener*/public void display(ImageView imageView,String url,DisplayConfig displayConfig,ImageListener listener){if (imageView == null){throw new NullPointerException("ImageView cannot be null");}//封装成一个请求对象BitmapRequest request= new BitmapRequest(imageView,url,displayConfig,listener);//加入请求队列requestQueue.addRequest(request);}/*** 监听图片,设置后期处理,仿Glide*/public static interface ImageListener{void onComplete(ImageView imageView, Bitmap bitmap,String url);}/*** 获取全局配置* @return*/public ImageLoaderConfig getConfig() {return config;}
}
3.BitmapRequest请求对象
一个BitmapRequest对象中封装了这次请求的所有相关信息,包括这个请求的图片显示逻辑的配置DisplayConfig,当前全局的加载策略LoadPolicy,请求图片的网络地址和这个地址需要展示的控件对象ImageView,另外我对BitmapRequest对象重写了hashCode和equals方法,实现了Comparable接口,以实现这个对象的比较逻辑,为加载策略服务
package com.rzm.commonlibrary.general.imageloader.request;import android.widget.ImageView;import com.rzm.commonlibrary.general.imageloader.config.DisplayConfig;
import com.rzm.commonlibrary.general.imageloader.loader.SimpleImageLoader;
import com.rzm.commonlibrary.general.imageloader.policy.LoadPolicy;
import com.rzm.commonlibrary.general.imageloader.utils.Md5Util;import java.lang.ref.SoftReference;/*** Author:renzhenming* Time:2018/6/13 7:23* Description:This is BitmapRequest*/
public class BitmapRequest implements Comparable<BitmapRequest>{/*** 展示配置*/private DisplayConfig disPlayConfig;/*** 加载策略*/private LoadPolicy loadPolicy = SimpleImageLoader.getInstance().getConfig().getLoadPolicy();/*** 序列号,用于顺序比较*/private int serialNum;/*** 持有ImageView的软引用*/private SoftReference<ImageView> imageViewSoftReference;/*** 图片路径*/private String imageUrl;/*** 图片路径的md5值*/private String imageUrlMd5;/*** 下载完成的监听*/private SimpleImageLoader.ImageListener imageListener;public BitmapRequest() {}public BitmapRequest(ImageView imageView, String imageUrl) {this(imageView,imageUrl,null,null);}public BitmapRequest(ImageView imageView,String imageUrl,DisplayConfig displayConfig) {this(imageView,imageUrl,displayConfig,null);}public BitmapRequest(ImageView imageView,String imageUrl,SimpleImageLoader.ImageListener imageListener) {this(imageView,imageUrl,null,imageListener);}public BitmapRequest(ImageView imageView, String imageUrl, DisplayConfig displayConfig,SimpleImageLoader.ImageListener imageListener){this.imageViewSoftReference = new SoftReference<ImageView>(imageView);if (imageUrl != null) {imageView.setTag(imageUrl);imageUrlMd5 = Md5Util.toMD5(imageUrl);}this.imageUrl = imageUrl;if (displayConfig != null){this.disPlayConfig = displayConfig;}if (imageListener != null) {this.imageListener = imageListener;}}/*** 请求的先后顺序是根据加载的策略进行的,不同的策略比较的条件也不同,所以* 这里要把比较的逻辑交割具体的策略去做,策略有多种,所以通过接口调用,增强扩展性* @return*/@Overridepublic int compareTo(BitmapRequest o) {return loadPolicy.compareTo(o,this);}public int getSerialNum() {return serialNum;}public void setSerialNum(int serialNum) {this.serialNum = serialNum;}/*** 获取这个请求对应的ImageView* @return*/public ImageView getImageView(){if (imageViewSoftReference == null)return null;return imageViewSoftReference.get();}public DisplayConfig getDisPlayConfig() {return disPlayConfig;}public LoadPolicy getLoadPolicy() {return loadPolicy;}public SoftReference<ImageView> getImageViewSoftReference() {return imageViewSoftReference;}public String getImageUrl() {return imageUrl;}public String getImageUrlMd5() {return imageUrlMd5;}public SimpleImageLoader.ImageListener getImageListener() {return imageListener;}/*** BitmapRequest会被加入请求队列中,在队列中有需要做判断当前请求是否存在* 那么就涉及到这个对象的比较,所以需要重写hashCode和equals方法*/@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;BitmapRequest request = (BitmapRequest) o;return serialNum == request.serialNum &&loadPolicy.equals(request.loadPolicy);}@Overridepublic int hashCode() {int result = loadPolicy != null ? loadPolicy.hashCode():0;result = 66*result+serialNum;return result;}}
4.分发器实现原理
RequestDispatcher是一个继承了Thread的线程对象,队列创建后会根据配置创建出指定数量的分发器,当队列中有请求对象后就从队列中取出对象交给加载器,根据一定的协议选择合适的加载器进行网络请求,当队列中没有对象时,会进入休眠状态
package com.rzm.commonlibrary.general.imageloader.request;import android.text.TextUtils;
import android.util.Log;import com.rzm.commonlibrary.general.imageloader.loader.Loader;
import com.rzm.commonlibrary.general.imageloader.loader.LoaderManager;import java.util.concurrent.BlockingQueue;/*** Author:renzhenming* Time:2018/6/13 7:24* Description:This is RequestDispatcher*/
public class RequestDispatcher extends Thread{/*** 从队列中转发请求需要持有队列的引用*/private BlockingQueue<BitmapRequest> blockingQueue;public RequestDispatcher(BlockingQueue<BitmapRequest> blockingQueue) {this.blockingQueue = blockingQueue;}/*** 阻塞式队列,转发器开启,从队列中取请求队列,如果没有则会阻塞当前线程,所以这里* 是在子线程开启的*/@Overridepublic void run() {while(!isInterrupted()){try {BitmapRequest request = blockingQueue.take();//处理请求对象,交给loaderString schema = parseSchema(request.getImageUrl());//获取加载器Loader loader = LoaderManager.getInstance().getLoader(schema);if (loader == null){Log.d("TAG",request.getImageUrl() + "没有找到对应的加载器");return;}loader.load(request);} catch (InterruptedException e) {e.printStackTrace();}}}/*** 根据图片url判断加载类型* @param imageUrl* @return*/private String parseSchema(String imageUrl) {if (TextUtils.isEmpty(imageUrl)){return null;}if (imageUrl.contains("://")){//形如 http://xxx 或者file://xxx,这样截取后//可以获得http file等前缀,根据这个前缀获取相应//的加载器return imageUrl.split("://")[0];}else{Log.d("TAG","不持支的图片类型");}return null;}
}
5.加载器实现原理
目前该框架支持网络图片和本地图片加载,不同的加载器根据不同的url进行选择,为了提高扩展性,设置接口和抽象类的实现方式,下面是顶层接口
package com.rzm.commonlibrary.general.imageloader.loader;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;/*** Author:renzhenming* Time:2018/6/13 7:21* Description:This is Loader*/
public interface Loader {/*** 加载图片* @param request*/void load(BitmapRequest request);
}
网络加载器和本地加载器实现逻辑有所不同,但是有一些公共的操作存在,比如加载前显示加载中占位图,加载失败显示失败图片等等,这些操作可以放在一个公共的基类中实现,所以这里创建了一个抽象类作为上层类处理公共逻辑
package com.rzm.commonlibrary.general.imageloader.loader;import android.graphics.Bitmap;
import android.widget.ImageView;import com.rzm.commonlibrary.general.imageloader.cache.BitmapCache;
import com.rzm.commonlibrary.general.imageloader.config.DisplayConfig;
import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;import java.util.concurrent.atomic.AtomicInteger;/*** Author:renzhenming* Time:2018/6/13 7:22* Description:This is AbstractLoader* 加载器策略不同,则不同的加载器实现方式不同,但是他们有相同的操作,比如显示* 占位图等,所以这些相同操作在抽象一层出来*/
public abstract class AbstractLoader implements Loader {private static final String TAG = "AbstractLoader";private AtomicInteger integer = new AtomicInteger(0);/*** 加载器加载图片的逻辑是先缓存后网络,所以需要持有缓存对象的引用*/private BitmapCache bitmapCache = SimpleImageLoader.getInstance().getConfig().getBitmapCache();/*** 同样因为要处理显示时的逻辑,所以需要持有显示配置对象的引用*/private DisplayConfig displayConfig = SimpleImageLoader.getInstance().getConfig().getDisplayConfig();@Overridepublic void load(BitmapRequest request) {//从缓存中获取BitmapBitmap bitmap= null;if (bitmapCache != null) {bitmap = bitmapCache.get(request);}if (bitmap == null){//显示加载中图片showLoadingImg(request);//开始加载网络图,加载的逻辑不同加载器有所不同,所以交给各自//加载器实现,抽象bitmap = onLoad(request);if (bitmap == null){//加载失败重试三次while(integer.incrementAndGet() <=3){bitmap = onLoad(request);if (bitmap != null){break;}}integer.set(0);}if (bitmap == null){}//加入缓存if (bitmapCache != null && bitmap != null)cacheBitmap(request,bitmap);}else{//有缓存}deliveryToUIThread(request,bitmap);}public abstract Bitmap onLoad(BitmapRequest request);protected void deliveryToUIThread(final BitmapRequest request, final Bitmap bitmap) {ImageView imageView = request.getImageView();if(imageView!=null) {imageView.post(new Runnable() {@Overridepublic void run() {updateImageView(request, bitmap);}});}}private void updateImageView(final BitmapRequest request, final Bitmap bitmap) {ImageView imageView = request.getImageView();//加载正常 防止图片错位if(bitmap != null && imageView.getTag().equals(request.getImageUrl())){imageView.setImageBitmap(bitmap);}//有可能加载失败if(bitmap == null && displayConfig!=null&&displayConfig.errorImage!=-1){imageView.setImageResource(displayConfig.errorImage);}//监听//回调 给圆角图片 特殊图片进行扩展if(request.getImageListener() != null){request.getImageListener().onComplete(imageView, bitmap, request.getImageUrl());}}/*** 缓存图片* @param request* @param bitmap*/private void cacheBitmap(BitmapRequest request, Bitmap bitmap) {if (request != null && bitmap != null){synchronized (AbstractLoader.class){bitmapCache.put(request,bitmap);}}}/*** 显示加载中占位图,需要判断用户有没有配置* @param request*/private void showLoadingImg(BitmapRequest request) {if (hasLoadingPlaceHolder()){final ImageView imageView = request.getImageView();if (imageView != null){imageView.post(new Runnable() {@Overridepublic void run() {imageView.setImageResource(displayConfig.loadingImage);}});}}}/*** 是否设置了加载中图片* @return*/private boolean hasLoadingPlaceHolder() {return displayConfig != null && displayConfig.loadingImage > 0;}
}
接下来是具体的加载器实现
package com.rzm.commonlibrary.general.imageloader.loader;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;
import com.rzm.commonlibrary.general.imageloader.utils.BitmapDecoder;
import com.rzm.commonlibrary.general.imageloader.utils.ImageViewHelper;import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;/*** Author:renzhenming* Time:2018/6/13 7:22* Description:This is UrlLoader* 网络图片加载器*/
public class UrlLoader extends AbstractLoader {@Overridepublic Bitmap onLoad(BitmapRequest request) {try {String imageUrl = request.getImageUrl();if (TextUtils.isEmpty(imageUrl)){return null;}URL url = new URL(imageUrl);HttpURLConnection conn = (HttpURLConnection) url.openConnection();if (conn.getResponseCode() != 200){return null;}InputStream inputStream = conn.getInputStream();//转化成BufferedInputStreamfinal BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);//标记一下,reset后会重置到这个位置bufferedInputStream.mark(inputStream.available());BitmapDecoder decoder = new BitmapDecoder() {@Overridepublic Bitmap decodeBitmapWithOptions(BitmapFactory.Options options) {//第一次读取,因为设置了inJustDecodeBounds为true,所以,这里decodeStream之后,会将宽高//信息存储在options中;第二次读取,因为设置了inJustDecodeBounds为false.所以会将流全部读取Bitmap bitmap = BitmapFactory.decodeStream(bufferedInputStream,null,options);if (options.inJustDecodeBounds){//表示时第一次执行,此时只是为了获取Boundstry {//第一次读取图片宽高信息,读完之后,要为第二次读取做准备,将流重置bufferedInputStream.reset();} catch (IOException e) {e.printStackTrace();}}else{try {bufferedInputStream.close();} catch (IOException e) {e.printStackTrace();}}return bitmap;}};//传入控件的宽高,设置图片适应控件return decoder.decodeBitmap(ImageViewHelper.getImageViewWidth(request.getImageView()),ImageViewHelper.getImageViewHeight(request.getImageView()));} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}
}
package com.rzm.commonlibrary.general.imageloader.loader;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;
import com.rzm.commonlibrary.general.imageloader.utils.BitmapDecoder;
import com.rzm.commonlibrary.general.imageloader.utils.ImageViewHelper;import java.io.File;/*** Author:renzhenming* Time:2018/6/13 7:22* Description:This is LocalLoader* 本地图片加载器*/
public class LocalLoader extends AbstractLoader {@Overridepublic Bitmap onLoad(BitmapRequest request) {//得到本地图片的路径final String path = Uri.parse(request.getImageUrl()).getPath();File file = new File(path);if (!file.exists() || !file.isFile()){return null;}BitmapDecoder decoder = new BitmapDecoder() {@Overridepublic Bitmap decodeBitmapWithOptions(BitmapFactory.Options options) {return BitmapFactory.decodeFile(path,options);}};return decoder.decodeBitmap(ImageViewHelper.getImageViewWidth(request.getImageView()),ImageViewHelper.getImageViewHeight(request.getImageView()));}
}
package com.rzm.commonlibrary.general.imageloader.utils;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;/*** 图片解码器*/
public abstract class BitmapDecoder {/*** 压缩图片* @param width imageView的宽度* @param height imageView的高度* @return*/public Bitmap decodeBitmap(int width,int height){BitmapFactory.Options options = new BitmapFactory.Options();//设置为true 只读取图片的宽高,不需要将整个图片都加载到内存options.inJustDecodeBounds = true;decodeBitmapWithOptions(options);//经过上面一次操作,此时options中已经有了宽高信息calculateSampleSizeWithOptions(options,width,height);//第二次就可以得到缩放后的bitmap了return decodeBitmapWithOptions(options);}/*** 将图片宽高和控件宽高进行比较,得到缩放值,信息仍然存储在options中* @param options* @param viewWidth* @param viewHeight*/private void calculateSampleSizeWithOptions(BitmapFactory.Options options,int viewWidth,int viewHeight) {//计算缩放比例//图片的原始宽高int width = options.outWidth;int height = options.outHeight;int inSampleSize = 1;//当图片的宽高大于控件的宽高时才需要压缩if (width > viewWidth || height > viewHeight){//计算出宽高的缩放比例int widthRatio = Math.round((float) width/(float)viewWidth);int heightRatio = Math.round((float)height/(float)viewHeight);//取宽高缩放比较大的值为图片的缩放比inSampleSize = Math.max(widthRatio,heightRatio);}//设置到options中,options保存的是配置信息//当inSampleSize为2,图片的宽高会缩放为原来的1/2options.inSampleSize = inSampleSize;//每个像素2个字节options.inPreferredConfig = Bitmap.Config.RGB_565;//宽高已经计算出来了,inJustDecodeBounds值可以复位了options.inJustDecodeBounds = false;//当系统内存不足时.可以回收bitmapoptions.inPurgeable = true;options.inInputShareable = true;}/*** 将流的处理通过抽象方法暴露出来,降低解码器和外部的耦合* @param options*/public abstract Bitmap decodeBitmapWithOptions(BitmapFactory.Options options);
}
6.本地缓存
package com.rzm.commonlibrary.general.imageloader.cache;import android.graphics.Bitmap;
import android.util.LruCache;import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;/*** Author:renzhenming* Time:2018/6/13 7:20* Description:This is MemoryCache*/
public class MemoryCache implements BitmapCache{private LruCache<String,Bitmap> mLruCache;public MemoryCache(){int maxSize = (int) (Runtime.getRuntime().maxMemory()/1024/8);mLruCache = new LruCache<String,Bitmap>(maxSize){@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes()*value.getHeight();}};}@Overridepublic Bitmap get(BitmapRequest request) {if (mLruCache == null) return null;return mLruCache.get(request.getImageUrlMd5());}@Overridepublic void put(BitmapRequest request, Bitmap bitmap) {if (mLruCache == null) return;mLruCache.put(request.getImageUrlMd5(),bitmap);}@Overridepublic void remove(BitmapRequest request) {if (mLruCache == null) return;mLruCache.remove(request.getImageUrlMd5());}
}
7.硬盘缓存
package com.rzm.commonlibrary.general.imageloader.disk;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;import com.rzm.commonlibrary.general.imageloader.cache.BitmapCache;
import com.rzm.commonlibrary.general.imageloader.request.BitmapRequest;
import com.rzm.commonlibrary.general.imageloader.utils.IOUtil;import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;/*** Author:renzhenming* Time:2018/6/13 7:20* Description:This is DiskCache*/
public class DiskCache implements BitmapCache {private static volatile DiskCache mDiskCache;//缓存路径private String mCacheDir = "Image";//MBprivate static final int MB = 1024 * 1024;//jackwharton的杰作private DiskLruCache mDiskLruCache;private DiskCache(Context context){iniDiskCache(context);}public static DiskCache getInstance(Context context) {if(mDiskCache==null){synchronized (DiskCache.class){if(mDiskCache==null){mDiskCache=new DiskCache(context);}}}return mDiskCache;}private void iniDiskCache(Context context) {//得到缓存的目录 android/data/data/com.xxx/cache/ImageFile directory=getDiskCache(mCacheDir,context);if(!directory.exists()){directory.mkdirs();}try {//最后一个参数 指定缓存容量mDiskLruCache=DiskLruCache.open(directory,1,1,50*MB);} catch (IOException e) {e.printStackTrace();}}private File getDiskCache(String mCacheDir, Context context) {//默认缓存路径return new File(context.getCacheDir(),mCacheDir);//return new File(Environment.getExternalStorageDirectory(),mCacheDir);}@Overridepublic void put(BitmapRequest request, Bitmap bitmap) {if (mDiskLruCache == null) return;DiskLruCache.Editor edtor=null;OutputStream os=null;try {//路径必须是合法字符edtor=mDiskLruCache.edit(request.getImageUrlMd5());os=edtor.newOutputStream(0);if(persistBitmap2Disk(bitmap,os)){edtor.commit();}else {edtor.abort();}} catch (IOException e) {e.printStackTrace();}}private boolean persistBitmap2Disk(Bitmap bitmap, OutputStream os) {BufferedOutputStream bos=new BufferedOutputStream(os);bitmap.compress(Bitmap.CompressFormat.JPEG,100,bos);try {bos.flush();} catch (IOException e) {e.printStackTrace();}finally {IOUtil.closeQuietly(bos);}return true;}@Overridepublic Bitmap get(BitmapRequest request) {if (mDiskLruCache == null) return null;try {DiskLruCache.Snapshot snapshot=mDiskLruCache.get(request.getImageUrlMd5());if(snapshot!=null){InputStream inputStream=snapshot.getInputStream(0);return BitmapFactory.decodeStream(inputStream);}} catch (IOException e) {e.printStackTrace();}return null;}@Overridepublic void remove(BitmapRequest request) {if (mDiskLruCache == null) return;try {mDiskLruCache.remove(request.getImageUrlMd5());} catch (IOException e) {e.printStackTrace();}}
}
这篇关于16.手写图片加载框架ImageLoader的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!