文章主要是仿照Glide方式来实现了图片加载,当然功能没有Glide那么全面,主要用来练习总结
写一个Glide框架,首先需要我们来分析一下实现思路,主要分了六大步骤,接下来我们会一步一步来实现操作
- 资源封装
- 活动缓存
- 内存缓存
- 磁盘缓存
- 生命周期
- 图片加载
资源封装
资源的封装主要是为了后面的缓存策略进行服务,方便查找放置,所以有了key,value,进行查找方便
Key:即为键,因为图片资源链接中可能会存在特殊符号,所以我们需要特殊处理下来保证没有特殊符号,所以我这里我进行了加密操作
Value:存放的图片,同时需要做一些记录操作,因为后面的缓存策略,我们需要做一些标记来进行应对
public class Key {
private String key;
/**
* 需要对key进行加密操作,因为key是url,所以会存在特殊符号的可能
* @param key
*/
public Key(String key) {
this.key = Utils.AES256Encode(key);
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
public class Value {
private static final String TAG = "Value";
private Bitmap mBitmap;
private ValueCallback callback;
/**
* 使用计数
*/
private int count;
private String key;
private static Value value;
public static Value getInstance() {
if (value == null) {
synchronized (Value.class) {
if (value == null) {
value = new Value();
}
}
}
return value;
}
private Value() {
}
/**
* 使用一次就进行 +1
*/
public void useAction() {
if (mBitmap == null) {
Log.i(TAG, "图片资源为null");
return;
}
if (mBitmap.isRecycled()) {
Log.i(TAG, "图片资源已经被回收");
return;
}
++count;
}
/**
* 使用一次后 -1
*/
public void nonUseAction() {
count--;
if (count <= 0 && callback != null) {
//不再使用了,需要告诉外界不再使用
callback.valueNonUseListener(key, this);
}
}
/**
* 释放资源
*/
public void recycleBitmap() {
if (count > 0) {
Log.i(TAG, "图片资源仍然在使用,不能将其释放");
return;
}
//表示被回收了,不用再回收
if (mBitmap.isRecycled()) {
Log.i(TAG, "图片资源已经被释放");
return;
}
mBitmap.recycle();
value = null;
System.gc();
}
public Bitmap getmBitmap() {
return mBitmap;
}
public void setmBitmap(Bitmap mBitmap) {
this.mBitmap = mBitmap;
}
public ValueCallback getCallback() {
return callback;
}
public void setCallback(ValueCallback callback) {
this.callback = callback;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
public interface ValueCallback {
void valueNonUseListener(String key, Value value);
}
资源的封装比较简单,只是做一些基本辅助工作
接下来主要是缓存策略的操作
活动缓存-内存缓存-磁盘缓存,复用池这里不做过多说明,Glide里面还有复用池的应用,以此能解决内存抖动的问题
其中活动缓存和内存缓存本质上来说都是在内存中进行操作,在界面上时,加载的图像会进入活动缓存,加载图片时我们会先从活动缓存中查找,如果没有,则从内存缓存中查找,这里有找到和没找到的情况,找的话我们将其从内存缓存中移除,放入活动缓存中,没找到的话我们继续从磁盘缓存中查找,找到的话放入活动缓存中,如果还没找到则需要从网络或者sd卡中寻找
当活动界面销毁时,活动缓存中的数据会进入内存缓存中,这里内存缓存和磁盘缓存我们都会进行LRU算法的操作
缓存的处理策略大概是这样,接下来让我们来看代码,首先是活动缓存
/**
* 活动缓存-正在被使用的资源
* 因为是GC回收机制,所以需要处理成弱引用
* 这里只有添加移除和获取三种操作
*/
public class ActiveCache {
private Map<String, WeakReference<Value>> mMap = new HashMap<>();
/**
* 为了监听弱引用是否被回收,CustomWeakReference中需要ReferenceQueue,所以我们需要获取这个queue
*/
private ReferenceQueue<Value> mQueue;
private boolean isCloseThread;
private Thread mThread;
private boolean isShutDownRemove;
private ValueCallback valueCallback;
public ActiveCache(ValueCallback valueCallback) {
this.valueCallback = valueCallback;
}
public void put(String key, Value value) {
if (TextUtils.isEmpty(key) || value == null) {
throw new IllegalStateException("传递的数据为空");
}
//绑定value的监听
value.setCallback(valueCallback);
mMap.put(key, new CustomWeakReference(value, getQueue(), key));
}
/**
* 获取资源
* @param key
* @return
*/
public Value get(String key) {
WeakReference<Value> valueWeakReference = mMap.get(key);
if (valueWeakReference != null) {
return valueWeakReference.get();
}
return null;
}
/**
* 手动移除
* @param key
* @return
*/
public Value remove(String key) {
isShutDownRemove = true;
WeakReference<Value> valueWeakReference = mMap.remove(key);
isShutDownRemove = false;
if (valueWeakReference != null) {
return valueWeakReference.get();
}
return null;
}
/**
* 释放关闭线程
*/
public void closeThread() {
isCloseThread = true;
if (mThread != null) {
mThread.interrupt();
try {
//线程稳定安全的停止
mThread.join(TimeUnit.SECONDS.toMillis(5));
//如果还是活跃状态
if (mThread.isAlive()) {
throw new IllegalStateException("线程关闭失败了");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 为了监听弱引用被回收,被动移除的
*/
private ReferenceQueue<Value> getQueue() {
if (mQueue == null) {
mQueue = new ReferenceQueue<>();
//监听这个弱引用,是否被回收了
mThread = new Thread() {
@Override
public void run() {
super.run();
while (!isCloseThread) {
try {
//如果被回收了,就会执行到这个方法
// mQueue.remove() 是阻塞式的方法,如果没有移除不会往下执行,如果移除了,下面的才会执行
Reference<? extends Value> remove = mQueue.remove();
CustomWeakReference weakReference = (CustomWeakReference) remove;
//移除容器中存储的,需要区分是主动移除还是被动移除的
if (!mMap.isEmpty() && !isShutDownRemove) {
mMap.remove(weakReference.key);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
mThread.start();
}
return mQueue;
}
/**
* 需要监听弱引用
*/
public static class CustomWeakReference extends WeakReference<Value> {
private String key;
/**
* 该方法能知道是否被回收掉
*/
public CustomWeakReference(Value referent, ReferenceQueue<? super Value> q, String key) {
super(referent, q);
this.key = key;
}
}
}
接下来是内存缓存
public class MemoryCache extends LruCache<String, Value> {
private boolean isShutDownRemove;
/**
* @param maxSize
* for caches that do not override {@link #sizeOf}, this is
* the maximum number of entries in the cache. For all other caches,
* this is the maximum sum of the sizes of the entries in this cache.
*/
public MemoryCache(int maxSize) {
super(maxSize);
}
private MemoryCacheCallback memoryCacheCallback;
public void setMemoryCacheCallback(MemoryCacheCallback memoryCacheCallback) {
this.memoryCacheCallback = memoryCacheCallback;
}
@Override
protected int sizeOf(String key, Value value) {
Bitmap bitmap = value.getmBitmap();
return bitmap.getAllocationByteCount();
}
/**
* 1.最少使用的元素会被移除
* 2.重复的key
*/
@Override
protected void entryRemoved(boolean evicted, String key, Value oldValue, Value newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
//被动的移除
if (memoryCacheCallback != null && !isShutDownRemove) {
memoryCacheCallback.entryRemovedMemoryCache(key, oldValue);
}
}
/**
* 当进入到活动缓存中时需要手动移除
*/
public Value shutDownRemove(String key) {
isShutDownRemove = true;
Value remove = remove(key);
isShutDownRemove = false;
return remove;
}
}
public interface MemoryCacheCallback {
/**
* 移除内存缓存中的Key
* @param key
* @param value
*/
void entryRemovedMemoryCache(String key, Value value);
}
磁盘缓存
public class DiskLruCacheImpl {
private static final String TAG = "DiskLruCacheImpl";
private static final String DISK_LRU_CACHE_DIR = "disk_lru_cache_dir";
private DiskLruCache diskLruCache;
/**
* 做标记,版本号变更的话之前的缓存失效
*/
private static final int APP_VERSION = 999;
private static final int VALUE_COUNT = 1;
private static final long MAX_SIZE = 1024 * 1024 * 10;
public DiskLruCacheImpl() {
File file = new File(Environment.getExternalStorageDirectory() + File.separator + DISK_LRU_CACHE_DIR);
try {
diskLruCache = DiskLruCache.open(file, APP_VERSION, VALUE_COUNT, MAX_SIZE);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 添加
*/
public void put(String key, Value value) {
DiskLruCache.Editor edit = null;
OutputStream outputStream = null;
try {
edit = diskLruCache.edit(key);
//index 不能大于VALUE_COUNT
outputStream = edit.newOutputStream(0);
Bitmap bitmap = value.getmBitmap();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
try {
edit.abort();
} catch (IOException ex) {
ex.printStackTrace();
}
} finally {
try {
edit.commit();
diskLruCache.flush();
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取
*/
public Value get(String key) {
if (TextUtils.isEmpty(key)) {
throw new IllegalStateException(TAG + " : key不能为空");
}
InputStream inputStream = null;
Value value = null;
try {
DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
if (snapshot != null) {
value = Value.getInstance();
inputStream = snapshot.getInputStream(0);
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
value.setmBitmap(bitmap);
value.setKey(key);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return value;
}
}
然后是网络请求或者Sd卡读取
public class LoadDataManager implements ILoadData, Runnable {
private String path;
private ResponseListener responseListener;
private Context context;
@Override
public Value loadResource(Context context, String path, ResponseListener listener) {
this.path = path;
this.responseListener = listener;
this.context = context;
//加载网络图片/SD卡本地图片
Uri uri = Uri.parse(path);
if ("HTTP".equalsIgnoreCase(uri.getScheme()) || "HTTPS".equalsIgnoreCase(uri.getScheme())) {
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10)).execute(this);
}
//Sd本地图片 找到图片返回
return null;
}
@Override
public void run() {
InputStream inputStream = null;
HttpURLConnection httpURLConnection = null;
try {
URL url = new URL(path);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(5000);
final int responseCode = httpURLConnection.getResponseCode();
if (responseCode == 200) {
inputStream = httpURLConnection.getInputStream();
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
//需要切换回主线程
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Value value = Value.getInstance();
value.setmBitmap(bitmap);
//回调成功
responseListener.responseSuccess(value);
}
});
} else {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
responseListener.responseFail(new Exception("请求失败,请求码" + responseCode));
}
});
}
} catch (final Exception e) {
e.printStackTrace();
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
responseListener.responseFail(e);
}
});
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
}
}
}
生命周期
生命周期的绑定我们主要是通过添加fragment,然后监听fragment的方式来达到监听生命周期的目的
public class RequestManager {
private static final String TAG = "RequestManager";
public static final String FRAGMENT_ACTIVITY_NAME = "fragment_activity_name";
public static final String ACTIVITY_NAME = "activity_name";
private Context requestManagerContext;
private static final int SEND_FRAGMENT_ACTIVITY_MSG = 999;
private static final int SEND_ACTIVITY_MSG = 888;
private RequestTargetEngine requestTargetEngine;
private FragmentActivityFragmentManager fragmentActivityByTag;
private ActivityFragmentManager activityByTag;
/**
* 可以管理生命周期 --fragment -- FragmentActivityFragmentManager
* @param fragmentActivity
*/
public RequestManager(FragmentActivity fragmentActivity) {
requestManagerContext = fragmentActivity;
FragmentManager supportFragmentManager = fragmentActivity.getSupportFragmentManager();
fragmentActivityByTag = (FragmentActivityFragmentManager) supportFragmentManager.findFragmentByTag(FRAGMENT_ACTIVITY_NAME);
if (fragmentActivityByTag == null) {
//关联生命周期
fragmentActivityByTag = new FragmentActivityFragmentManager(requestTargetEngine);
supportFragmentManager.beginTransaction()
.add(fragmentActivityByTag, FRAGMENT_ACTIVITY_NAME)
.commitAllowingStateLoss();
}
//发送一个handler,因为add,这种操作会存在等待情况,需要确保能拿到
mHandler.sendEmptyMessage(SEND_FRAGMENT_ACTIVITY_MSG);
}
{
//构造代码块中初始化,避免每个都需要初始化
requestTargetEngine = new RequestTargetEngine();
}
/**
* 可以管理生命周期 --activity -- ActivityFragmentManager
* @param activity
*/
public RequestManager(Activity activity) {
requestManagerContext = activity;
android.app.FragmentManager fragmentManager = activity.getFragmentManager();
activityByTag = (ActivityFragmentManager) fragmentManager.findFragmentByTag(ACTIVITY_NAME);
if (activityByTag == null) {
//关联生命周期
activityByTag = new ActivityFragmentManager(requestTargetEngine);
fragmentManager.beginTransaction().add(activityByTag, ACTIVITY_NAME).commitAllowingStateLoss();
}
//发送一个handler,因为add,这种操作会存在等待情况,需要确保能拿到
mHandler.sendEmptyMessage(SEND_ACTIVITY_MSG);
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i(TAG, "收到消息了" + msg.what);
if (msg.what == SEND_ACTIVITY_MSG) {
activityByTag.setRequestManager(RequestManager.this);
mHandler.removeMessages(SEND_ACTIVITY_MSG);
} else {
fragmentActivityByTag.setRequestManager(RequestManager.this);
mHandler.removeMessages(SEND_FRAGMENT_ACTIVITY_MSG);
}
}
};
/**
* 无法管理生命周期--application
* @param context
*/
public RequestManager(Context context) {
requestManagerContext = context;
}
/**
* 拿到图片路径
* @param path
* @return
*/
public RequestTargetEngine load(String path) {
requestTargetEngine.loadValueInitAction(requestManagerContext, path);
return requestTargetEngine;
}
}
真正处理的地方
public class RequestTargetEngine implements LifecyclerCallback, ValueCallback, MemoryCacheCallback, ResponseListener {
private static final String TAG = "RequestTargetEngine";
private static final int MEMORY_MAX_SIZE = 1024 * 1024 * 60;
@Override
public void glideInitAction() {
Log.i(TAG, "glideInitAction");
}
@Override
public void glideStopAction() {
Log.i(TAG, "glideStopAction");
}
@Override
public void glideRecycleAction() {
Log.i(TAG, "glideRecycleAction");
if (activeCache != null) {
//释放掉活动缓存
activeCache.closeThread();
}
}
private ActiveCache activeCache;
private MemoryCache memoryCache;
private DiskLruCacheImpl diskLruCache;
private Context context;
private String path;
private String key;
private ImageView imageView;
public void into(ImageView imageView) {
if (imageView == null) {
throw new IllegalStateException("对象不能为空");
}
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("需要在主线程中操作");
}
this.imageView = imageView;
//开始去加载资源 缓存-》网络 加载后放入缓存中
Value value = cacheAction();
if (value != null) {
imageView.setImageBitmap(value.getmBitmap());
//使用后-1
value.nonUseAction();
}
}
/**
* 活动缓存-内存缓存-磁盘缓存-网络
*/
private Value cacheAction() {
//1判断活动缓存中是否有对象资源,有就返回,没有就继续查找
Value value = activeCache.get(key);
if (value != null) {
Log.i(TAG, "在活动缓存中获取到数据");
//使用了一次,需要+1
value.useAction();
return value;
}
//2内存缓存中查找
value = memoryCache.get(key);
if (value != null) {
Log.i(TAG, "在内存缓存中获取到数据,需要将其移动到活动缓存中,并删除内存缓存中的数据");
//移动到活动缓存中
memoryCache.remove(key);
activeCache.put(key, value);
value.useAction();
return value;
}
//3从磁盘缓存中查找,找到就放到活动和内存缓存中,
value = diskLruCache.get(key);
if (value != null) {
activeCache.put(key, value);
value.useAction();
// memoryCache.put(key, value);
return value;
}
//4 加载网络/SD卡图片
value = new LoadDataManager().loadResource(context, path, this);
if (value != null) {
return value;
}
return null;
}
/**
* RequestManager传递的值
*/
public void loadValueInitAction(Context context, String path) {
this.context = context;
this.path = path;
key = new Key(path).getKey();
}
public RequestTargetEngine() {
if (activeCache == null) {
activeCache = new ActiveCache(this);
}
if (memoryCache == null) {
memoryCache = new MemoryCache(MEMORY_MAX_SIZE);
memoryCache.setMemoryCacheCallback(this);
}
if (diskLruCache == null) {
diskLruCache = new DiskLruCacheImpl();
}
}
/**
* 活动缓存中数据不再使用时触发
* @param key
* @param value
*/
@Override
public void valueNonUseListener(String key, Value value) {
//说明活动缓存中不再使用了,需要放到内存缓存中
if (!TextUtils.isEmpty(key) && value != null) {
memoryCache.put(key, value);
}
}
/**
* 内存缓存中被移除会触发这里
* @param key
* @param value
*/
@Override
public void entryRemovedMemoryCache(String key, Value value) {
//
}
/**
* 外部资源的成功
* @param value
*/
@Override
public void responseSuccess(Value value) {
if (value != null) {
imageView.setImageBitmap(value.getmBitmap());
saveCache(key, value);
}
}
/**
* 外部资源的失败
* @param e
*/
@Override
public void responseFail(Exception e) {
Log.i(TAG, "加载外部资源发生了异常" + e.getMessage());
}
/**
* 加载外部资源后保存到缓存中
* @param key
* @param value
*/
private void saveCache(String key, Value value) {
value.setKey(key);
if (diskLruCache != null) {
//保存到磁盘缓存中
diskLruCache.put(key, value);
}
}
}