一.资源编写
这块主要是针对缓存的,主要有缓存的path-key的转换,以及key所对应的bitmap的value,并且我们在value中使用计数器的方式进行回收,若无使用可回收bitmap,并且提供回调。
public class KeyBitMap {
private String key; //对请求地址的进行SHA256得到特定的值
public KeyBitMap(String key) {
this.key = Tools.getSHA256StrJava(key);
}
public void setKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
}
public class ValueBitMap {
private ValueCallBack valueCallBack;
public void setValueCallBack(ValueCallBack valueCallBack) {
this.valueCallBack = valueCallBack;
}
/**
* 这里做了单例?
*
* @return
*/
private String key;
private Bitmap bitmap;
private int countUse = 0; //是否被使用标记
/**
* 使用1次计数1次
*/
public void useAction() {
Tools.checkEmpty(bitmap);
if (bitmap.isRecycled()) {
System.out.println("ValueBitMap useAction bitmap has recycle");
return;
}
countUse++;
}
/**
* 不使用
*/
public void nonUseAction() {
countUse--;
if (countUse <= 0 && valueCallBack != null) {
//回调不在使用
valueCallBack.nonUseListener(key, this);
// recycleBitMap();
}
}
/**
* 释放bitmap
*/
public void recycleBitMap() {
System.out.println("ValueBitMap recycleBitMap recycleBitMap");
if (countUse > 0) {
System.out.println("ValueBitMap recycleBitMap bitmap 还再用,不可以用回收");
return;
}
if (bitmap.isRecycled()) {
System.out.println("recycleBitMap 已经被回收了");
return;
}
bitmap.recycle();
System.gc();
}
public Bitmap getBitmap() {
return bitmap;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
}
public interface ValueCallBack {
/**
* 计数器清0回调
* @param key
* @param valueBitMap
*/
void nonUseListener(String key,ValueBitMap valueBitMap);
}
二.缓存策略的三条线
gilde的缓存策略:
1.活动缓存:正在使用的缓存,加载时优先查早活动缓存,若无继续往下查找,它的回收策略是弱引用和gc,回收后放置内存缓存中,来源可以是内存缓存或磁盘缓存,注意内存缓存找到后会清楚掉内存缓存;
2.内存缓存:遵循lru算法的缓存,为活动缓存提供资源来源,非手动移除的资源会被放置Bitmap缓存池,如果内存缓存找不到会从磁盘缓存中获取,如果找到了提供给给活动缓存,并手动移除;
3.磁盘缓存:遵循lru算法的缓存,如果找到了直接提供给活动缓存,并且可以复用bitmap缓存池中的bitmap,如果未找到就去网络中加载;
4.本地/网络:加载资源,就没有什么好说的了。
三.活动缓存
- 数据结构:使用
HashMap<Key, ResourceWeakReference>存储资源,其中ResourceWeakReference是弱引用(WeakReference)的子类,指向图片资源对象。 - 引用管理:
-
- 弱引用确保资源未被强引用持有时可被GC回收。
- 配合
ReferenceQueue监听回收事件,自动清理无效缓存项。
- 引用计数:
-
- 同一资源被多个
ImageView使用时,引用计数递增。 - 当引用归零时,资源从活动缓存手动移除并转移到内存LRU缓存。
- 同一资源被多个
- 工作流程
-
- 资源被使用时加入活动缓存 :
activeResources.activate(key, resource)。 - 资源释放时移出:
activeResources.deactivate(key)。
- 资源被使用时加入活动缓存 :
public class ActiveCache {
private boolean isManuRemove = false; //是否手动移除
private Thread checkGcThread;
private boolean stopCheckGc = false;
//活动缓存的容器
private Map<String, WeakReference<ValueBitMap>> activeMap = new HashMap<>();
//GC中的移除
private ReferenceQueue<ValueBitMap> removeReferQueue;
private ValueCallBack valueCallBack;
public ActiveCache(ValueCallBack valueCallBack) {
this.valueCallBack = valueCallBack;
}
private ReferenceQueue getReferenceQueue() {
if (null == removeReferQueue) {
removeReferQueue = new ReferenceQueue<>();
//检测GC移除
checkGcThread = new Thread(new Runnable() {
@Override
public void run() {
while (!stopCheckGc) {
try {
if (!isManuRemove) { //判断下是否是手动的,如果是手动的就不移除了
//如果gc中有数据就会执行,不然就阻塞在此
Reference<? extends ValueBitMap> remove = removeReferQueue.remove();
ValueBitMap value = remove.get();
if (activeMap == null && !activeMap.isEmpty()) {
System.out.println("ActiveCache gc remove");
activeMap.remove(value.getKey());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
checkGcThread.start();
}
return removeReferQueue;
}
/**
* 1.中断检测gc
* 2.清除活动缓存
* 3.gc
*/
public void stopCheckGc() {
System.out.println("ActiveCache stopCheckGc");
stopCheckGc = true;
// if (null != checkGcThread) {
// checkGcThread.interrupt(); //手动中断线程
// try {
// checkGcThread.join(TimeUnit.SECONDS.toMillis(5));//5秒 线程稳定安全停止
// if (checkGcThread.isAlive()) { //如果线程依旧未关闭
// throw new IllegalStateException("活动缓存中线程关闭异常");
// }
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
activeMap.clear();
System.gc();
}
/**
* 添加到活动缓存
*
* @param key 加密后的key
* @param value
*/
public void put(String key, ValueBitMap value) {
// System.out.println("this:"+this);
Tools.checkEmpty(key);
value.setValueCallBack(valueCallBack);
WeakReference<ValueBitMap> valueBitMapWeakReference = new ValueWeakReference(value, getReferenceQueue(), key);
activeMap.put(key, valueBitMapWeakReference);
// System.out.println("ActiveCache put get: key "+ key+",value:"+valueBitMapWeakReference.get());
}
/**
* 获取活动缓存
*
* @param key
* @return
*/
public ValueBitMap get(String key) {
ValueBitMap valueBitMap = null;
WeakReference<ValueBitMap> valueBitMapWeakReference = activeMap.get(key);
// System.out.println("ActiveCache get: get1 "+ key+",valueBitMapWeakReference:"+valueBitMapWeakReference);
if (null != valueBitMapWeakReference) {
valueBitMap = valueBitMapWeakReference.get();
}
//System.out.println("ActiveCache get: get2 "+ key+",value:"+valueBitMap);
return valueBitMap;
}
/**
* 手动移除
*
* @param key 加密后的key
* @return
*/
public ValueBitMap manualRemove(String key) {
System.out.println("ActiveCache manualRemove: key "+ key);
isManuRemove = true;
WeakReference<ValueBitMap> valueBitMapWeakReference = activeMap.remove(key);
isManuRemove = false;
if (null != valueBitMapWeakReference) {
return valueBitMapWeakReference.get();
}
return null;
}
class ValueWeakReference extends WeakReference<ValueBitMap> {
private String key;
public ValueWeakReference(ValueBitMap referent, ReferenceQueue<? super ValueBitMap> q, String key) {
super(referent, q);
this.key = key;
}
}
}
四.内存缓存
依靠lru算法,若是非手动移除,回调移除对象。
public class MemoryCache extends LruCache<String, ValueBitMap> {
public void setCacheCallBack(MemoryCacheCallBack cacheCallBack) {
this.cacheCallBack = cacheCallBack;
}
private boolean isManuRemove = false;
/**
* 手动移除内存缓存
*
* @param key
* @return
*/
public ValueBitMap manuRemove(String key) {
isManuRemove = true;
ValueBitMap valueBitMap = remove(key);
isManuRemove = false;
return valueBitMap;
}
private MemoryCacheCallBack cacheCallBack;
public MemoryCache(int maxSize) {
super(maxSize);
}
/**
* 内存缓存被移除
*/
@Override
protected void entryRemoved(boolean evicted, String key, ValueBitMap oldValue, ValueBitMap newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
if (null != cacheCallBack && !isManuRemove) {
System.out.println("MemoryCache entryRemoved");
cacheCallBack.onMemoryRemoveListener(key, oldValue);
}
}
@Override
protected int sizeOf(String key, ValueBitMap value) {
Bitmap bitmap = value.getBitmap();
return Tools.getBitmapSize(bitmap);
}
public interface MemoryCacheCallBack {
void onMemoryRemoveListener(String key, ValueBitMap valueBitMap);
}
}
五.磁盘缓存
磁盘缓存:是对jakeWharton的DiskLruCache的二次封装,提供读取,写入功能
public class DiskCache {
private BitmapPool bitmapPool = BitmapPoolImpl.getInstance();
private final String DISK_PATH = "disk_lru_dir";
private final long CACHE_SIZE = 10_1024_1024;
private final int APP_VERSION = 0; //改了后 缓存会失效
private final int VALUE_COUNT = 1;//通常是1
private DiskLruCache diskLruCache;
public DiskCache() {
File file = new File(Environment.getExternalStorageDirectory()+
File.separator + DISK_PATH);
try {
System.out.println("filePath:"+file.getPath());
diskLruCache = DiskLruCache.open(file, APP_VERSION, VALUE_COUNT, CACHE_SIZE);
} catch (IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
public void put(String key, ValueBitMap valueBitMap) {
DiskLruCache.Editor editor = null;
OutputStream outputStream = null;
try {
editor =
diskLruCache.edit(key);
outputStream = editor.newOutputStream(0); //index不能大于valueCount
valueBitMap.getBitmap().compress(Bitmap.CompressFormat.PNG, 100, outputStream); //将bitmap写入到磁盘文件中去
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
try {
editor.abort();
} catch (IOException ex) {
System.out.println("editor 提交失败");
e.printStackTrace();
}
} finally {
try {
editor.commit();
} catch (IOException e) {
e.printStackTrace();
System.out.println("editor commit提交失败");
}
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println();
}
}
}
}
public ValueBitMap get(String key) {
Tools.checkEmpty(key);
ValueBitMap valueBitMap = new ValueBitMap();
DiskLruCache.Snapshot snapshot;
InputStream inputStream = null;
try {
snapshot = diskLruCache.get(key);
if (null != snapshot) {
inputStream = snapshot.getInputStream(0);
//--------------------缓存池的策略有问题,算了不处理了,大概知道原理即可---------------------------
// 将输入流的数据读取到字节数组中 只读不移动
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
byte[] imageData = byteArrayOutputStream.toByteArray();
// 第一次获取图片大小
BitmapFactory.Options options = new BitmapFactory.Options(); //入参出参对象
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options); //获取下图片的大小
int outHeight = options.outHeight;
int outWidth = options.outWidth;
System.out.println("outHeight:" + outHeight + ",outWidth:" + outWidth);
BitmapFactory.Options options2 = new BitmapFactory.Options(); //入参出参对象
boolean hitPool = false;
Bitmap bitmapPoolBitmap = bitmapPool.getBitmap(outWidth, outHeight, Bitmap.Config.RGB_565);
if (bitmapPoolBitmap == null) {
System.out.println("not found in pool---->>>");
// //如果缓存池没有 就用解析出来的
hitPool = false;
} else {
System.out.println(" found in pool---->>>");
hitPool = true;
}
//如果inBitmap设置为null,内部就会申请新的内存空间,无法复用,如果缓存中没有肯定是的
options2.inBitmap = bitmapPoolBitmap;//把复用池的bitMap给inBitMap
options2.inPreferredConfig = Bitmap.Config.RGB_565;
options2.inMutable = true;
options2.inJustDecodeBounds = false;
final Bitmap bitmap2 = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options2);//复用的是内存
if(!hitPool){
bitmapPool.putBitmap(bitmap2);
}
valueBitMap.setBitmap(bitmap2);
//-----------------------------------------------
// valueBitMap.setBitmap(BitmapFactory.decodeStream(inputStream));
valueBitMap.setKey(key);
System.out.println("本地磁盘获取成功");
return valueBitMap;
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("本地磁盘获取失败!");
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println("本地磁盘获取失败!");
}
}
}
return null;
}
}
六.生命周期的感知
生命周期的原理是通过在Activity中创建一个空的fragment,在fragment的生命周期变化中操作glide加载引擎。主要是由RequestRetriever、RuquestManager、RequestEngin、以及生命周期回调接口ActivityFragment,各自的职责为:
1.RequestRetriever提供RuquestManager;
2.RuquestManager添加感知生命周期的fragment,并且提供可以感知生命周期的RequestEngin,所以RequestEngin持有生命接口周期的回调或者直接继承生命周期回调接口ActivityFragment,在创建感知生命周期的fragment时直接传入RequestEngin,当有生命周期变化时直接回调其对应的方法。
1.生命周期感知接口
public interface MyActivityFragmentLife {
void glideInitAction();
void glideStopAction();
void glideRecycleAction();
}
2.生命周期提供者:持有感知接口
public class MyFragmentActivityFragment extends Fragment {
private MyActivityFragmentLife myActivityFragmentLife;
public MyFragmentActivityFragment() {
}
public MyFragmentActivityFragment(RequestEngin requestEngin) {
this.myActivityFragmentLife = requestEngin;
}
@Override
public void onStart() {
super.onStart();
if (null != myActivityFragmentLife) {
myActivityFragmentLife.glideInitAction();
}
}
@Override
public void onStop() {
super.onStop();
if (null != myActivityFragmentLife) {
myActivityFragmentLife.glideStopAction();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (null != myActivityFragmentLife) {
myActivityFragmentLife.glideRecycleAction();
}
}
}
3.感知生命周期的回调实现(加载引擎)
public class RequestEngin implements MyActivityFragmentLife {
private final ActiveCache activeCache;
private final MemoryCache memoryCache;
private DiskCache diskCache;
private final int MEMORY_CACHE_SIZE = 10 * 1024 * 1024;
private ImageView imageView;
private String path;
private String key;
private Context glideContext;
private ILoader iLoader;
private BitmapPool bitmapPool = BitmapPoolImpl.getInstance();
public RequestEngin() {
activeCache = new ActiveCache(new ValueCallBack() {
@Override
public void nonUseListener(String key, ValueBitMap valueBitMap) {
//引用归0时回调 活动缓存回收了
System.out.println("活动缓存手动回收"+key+",value:"+valueBitMap);
//1.手动移除
activeCache.manualRemove(key);
//2.放到内存缓存中去
memoryCache.put(key, valueBitMap);
}
});
memoryCache = new MemoryCache(MEMORY_CACHE_SIZE);
memoryCache.setCacheCallBack(new MemoryCache.MemoryCacheCallBack() {
@Override
public void onMemoryRemoveListener(String key, ValueBitMap valueBitMap) {
System.out.println("内存缓存被回收了");
//内存缓存被动丢出 加入到复用池
bitmapPool.putBitmap(valueBitMap.getBitmap());
}
});
diskCache = new DiskCache();
}
@Override
public void glideInitAction() {
System.out.println("glideInitAction");
}
@Override
public void glideStopAction() {
System.out.println("glideStopAction");
}
@Override
public void glideRecycleAction() {
System.out.println("glideRecycleAction");
if (null != activeCache) {
activeCache.stopCheckGc();
}
}
public void loadValueInitAction(String path, Context requestContext) {
this.path = path;
this.glideContext = requestContext;
this.key = new KeyBitMap(this.path).getKey();//缓存存储的key
}
public void into(ImageView imageView) {
Tools.checkEmpty(imageView);
Tools.assertMainThread();
this.imageView=imageView;
ValueBitMap value = cacheAction();//从缓存中获取
if (null != value) { //从缓存中获取到
imageView.setImageBitmap(value.getBitmap());
value.nonUseAction();//模拟完成之后减一 这个时候会回调删除活动缓存
}
}
/**
* 1.活动缓存去找,活动缓存自动移除后会移动到内存缓存中去
* 2.内存中去找,找到了手动移除内存缓存后,放置活动缓存 LRU回收后放到Bitmap缓存池中
* 3.磁盘缓存中去找,找到后放置活动缓存中去
* 4.网络加载,保存到磁盘中去
*
* @return
*/
private ValueBitMap cacheAction() {
ValueBitMap valueBitMap = activeCache.get(key);
System.out.println("cacheAction key:"+key);
if (null != valueBitMap) {
valueBitMap.useAction();//使用加1
System.out.println("cacheAction 使用活动缓存");
return valueBitMap;
}
valueBitMap = memoryCache.get(key);
if (null != valueBitMap) {
activeCache.put(key, valueBitMap); //
memoryCache.manuRemove(key);//手动移除 节省空间 因为已经移动到活动缓存了
System.out.println("cacheAction 使用内存缓存 使用后放到活动缓存中去 移除内存缓存");
valueBitMap.useAction();
return valueBitMap;
}
valueBitMap = diskCache.get(key);
if (null != valueBitMap) {
System.out.println("cacheAction 使用磁盘缓存:"+valueBitMap.getKey());
activeCache.put(key, valueBitMap);//放到活动缓存中去
System.out.println("cacheAction 使用磁盘缓存,使用完后放到活动缓存中去 不移除本地缓存");
valueBitMap.useAction();
return valueBitMap;
}
if (null == iLoader) {
iLoader = new LoadDataManager();
}
//加载
valueBitMap = iLoader.loadResource(path, glideContext, new ILoader.ResponseListener() {
@Override
public void onLoadSuccess(ValueBitMap valueBitMap) {
//网络加载成功的回调
if (null != valueBitMap) {
System.out.println("网络加载成功回调显示");
valueBitMap.setKey(key);
imageView.setImageBitmap(valueBitMap.getBitmap());
saveCache(key, valueBitMap);
}
}
@Override
public void onLoadError(Exception exception) {
}
});
//本地成功的
if (null != valueBitMap) {
return valueBitMap;
}
return null;
}
private void saveCache(String key, ValueBitMap valueBitMap) {
System.out.println("网络加载成功 放到本地缓存 key:"+key);
valueBitMap.setKey(key);
diskCache.put(key, valueBitMap);
}
}
4.组装生命周期感知架构类(组合生命周期以及感知回调)
public class RequestManager {
private Context requestContext;
private final String FRAGMENT_ACTIVITY_FRAGMENT_NAME = "fragment_activity_fragment_name";
private final String FRAGMENT_ACTIVITY_NAME = "fragment_activity_name";
private static RequestEngin requestEngin;
{
if (null == requestEngin) {
requestEngin = new RequestEngin();
}
}
public RequestManager(FragmentActivity fragmentActivity) {
this.requestContext = fragmentActivity;
FragmentManager supportFragmentManager = fragmentActivity.getSupportFragmentManager();
androidx.fragment.app.Fragment fragmentByTag = supportFragmentManager.findFragmentByTag(FRAGMENT_ACTIVITY_FRAGMENT_NAME);
if (fragmentByTag == null) {
System.out.println("create MyFragmentActivityFragment----->>>>");
fragmentByTag = new MyFragmentActivityFragment(requestEngin);
supportFragmentManager.beginTransaction().add(fragmentByTag, FRAGMENT_ACTIVITY_FRAGMENT_NAME).commitAllowingStateLoss();
}
new Handler(Looper.getMainLooper()).sendEmptyMessage(1);
}
public RequestManager(Activity activity) {
this.requestContext = activity;
android.app.FragmentManager fragmentManager = activity.getFragmentManager();
Fragment fragmentByTag = fragmentManager.findFragmentByTag(FRAGMENT_ACTIVITY_NAME);
if (fragmentByTag == null) {
fragmentByTag = new MyFragmentActivity(requestEngin);
fragmentManager.beginTransaction().add(fragmentByTag, FRAGMENT_ACTIVITY_NAME).commitAllowingStateLoss();
}
new Handler(Looper.getMainLooper()).sendEmptyMessage(1);
}
public RequestManager(Context context) {
this.requestContext = context;
}
public RequestEngin load(String path) {
requestEngin.loadValueInitAction(path, requestContext);
System.out.println("load requestEngin:" + requestEngin);
return requestEngin;
}
}
5.架构提供类(暴漏给glide使用)
public class RequestRetriever {
public RequestManager get(FragmentActivity fragmentActivity) {
return new RequestManager(fragmentActivity);
}
public RequestManager get(Activity activity) {
return new RequestManager(activity);
}
public RequestManager get(Context context) {
return new RequestManager(context);
}
}
七.调用实现流程
我们先来看看调用的代码:
Glide.with(this)//返回requestManager
.load("https://tupian.qqw21.com/article/UploadPic/2018-3/20183162315429465.jpg")//返回requesetEngit
.into(imageView);
1.我们的glide需要提供一个with方法,并且它返回的是requestManager,由于我们前面对于requestManager是通过requesetRetriever暴露的,所以我们在glide中提供一个requesetRetriever获取的方法,代码如下:
public class Glide {
private RequestRetriever requestRetriever;
public Glide(RequestRetriever requestRetriever) {
this.requestRetriever = requestRetriever;
}
public RequestRetriever getRequestRetriever() {
return requestRetriever;
}
//通过gildeBuilder构建glide,并且在glideBuilder中调用glide的构造方法,传入RequestRetriever
public static Glide get(Context context) {
return new Glide.Builder().builder();
}
public static RequestRetriever getRetriever(Context context) {
return Glide.get(context).getRequestRetriever();
}
public static RequestManager with(FragmentActivity fragmentActivity) {
return getRetriever(fragmentActivity).get(fragmentActivity);
}
public static RequestManager with(Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(Context context) {
return getRetriever(context).get(context);
}
public static class Builder {
Glide builder() {
RequestRetriever requestRetriever1 = new RequestRetriever();
return new Glide(requestRetriever1);
}
}
}
2.我们继续看下load方法,主要就是requesetManager中的load方法,我们可以看下感知生命周期的RequesetManager这个类,调用load方法时其实就是调用requestEngin中的load方法,并且返回了这个requestEngin这个对象,这里其实只是做些加载准备;
3.into的方法才是真正去加载图片,那其实就是缓存策略的流程了,这里就不在赘述。