文章主要是仿照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);
}
}
}