16.手写图片加载框架ImageLoader

2023-12-30 03:40

本文主要是介绍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.图片下载时请求转发

img_f1fc6c053236a29dbfbe643a3dc00c74.png
ImageLoader类图.png
框架构建流程

如上图,首先使用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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/551600

相关文章

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

GSON框架下将百度天气JSON数据转JavaBean

《GSON框架下将百度天气JSON数据转JavaBean》这篇文章主要为大家详细介绍了如何在GSON框架下实现将百度天气JSON数据转JavaBean,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录前言一、百度天气jsON1、请求参数2、返回参数3、属性映射二、GSON属性映射实战1、类对象映

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

使用Python实现无损放大图片功能

《使用Python实现无损放大图片功能》本文介绍了如何使用Python的Pillow库进行无损图片放大,区分了JPEG和PNG格式在放大过程中的特点,并给出了示例代码,JPEG格式可能受压缩影响,需先... 目录一、什么是无损放大?二、实现方法步骤1:读取图片步骤2:无损放大图片步骤3:保存图片三、示php

SpringBoot加载profile全面解析

《SpringBoot加载profile全面解析》SpringBoot的Profile机制通过多配置文件和注解实现环境隔离,支持开发、测试、生产等不同环境的灵活配置切换,无需修改代码,关键点包括配置文... 目录题目详细答案什么是 Profile配置 Profile使用application-{profil

解决若依微服务框架启动报错的问题

《解决若依微服务框架启动报错的问题》Invalidboundstatement错误通常由MyBatis映射文件未正确加载或Nacos配置未读取导致,需检查XML的namespace与方法ID是否匹配,... 目录ruoyi-system模块报错报错详情nacos文件目录总结ruoyi-systnGLNYpe

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.