【小資說庫】第13期 應用程序開發人員、DBA和DBMS開發人員的分工是怎樣的?
1206
2022-05-28
Glide的簡單使用
Glide里的緩存
默認情況下,Glide 會在開始一個新的圖片請求之前檢查以下多級的緩存:
活動資源 (Active Resources) - 現在是否有另一個 View 正在展示這張圖片?
內存緩存 (Memory cache) - 該圖片是否最近被加載過并仍存在于內存中?
資源類型(Resource) - 該圖片是否之前曾被解碼、轉換并寫入過磁盤緩存?
數據來源 (Data) - 構建這個圖片的資源是否之前曾被寫入過文件緩存?
前兩步檢查圖片是否在內存中,如果是則直接返回圖片。后兩步則檢查圖片是否在磁盤上,以便快速但異步地返回圖片。
如果四個步驟都未能找到圖片,則Glide會返回到原始資源以取回數據(原始文件,Uri, Url等)。
什么是三級緩存?
內存緩存:優先加載,速度最快
本地緩存:其次加載,速度快
網絡緩存:最后加載,速度慢,浪費流量
緩存機制
Glide使用了ActiveResources(活動緩存弱引用)+MemoryCache(內存緩存Lru算法)+DiskCache(磁盤緩存Lru算法)。
ActiveResources:存儲當前界面使用到的圖片。界面不展示后,該Bitmap又被緩存至MemoryCache中,并從ActiveResources中刪除。
Memory Cache:存儲當前沒有使用到的Bitmap,當MemoryCache中得到Bitmap后,該Bitmap又被緩存至ActiveResources中,并從MemoryCache中刪除。
Disk Cache:持久緩存。例如圖片加圓角,處理后圖片會被緩存到文件中,應用被再次打開時可以加載緩存直接使用。
注意: ActiveResources + MemoryCache是內存緩存,都屬于運行時緩存,且互斥(同一張圖片不會同時緩存在ActiveResources+MemoryCache),應用被殺死后將不存在。
Glide 內部是使用 LruCache、弱引用和硬盤緩存實現的。
Glide 主要將緩存分為兩塊內存緩存和硬盤緩存,兩種緩存的結合,構成了 Glide 緩存機制的核心。
為何設計出活動緩存
因為內存緩存使用LRU算法,當你使用Gilde加載并顯示第一張圖片時,后面又加載了很多圖片,同時你的第一張圖片還在用。這個時候內存緩存根據LRU算法可能會刪除你正在使用的第一張照片。這樣的后果就是你正在使用的照片找不到,后果就是程序崩潰。
加載流程
流程就是這么個流程下面咱們通過源碼加深一下。
Glide源碼
加載流程
1.Engine類
負責啟動加載并管理活動資源和緩存資源,它里面有個load方法。沒錯就是提供路徑加載圖片的方法。
2.load方法
這個方法里面滿滿的干貨。
public?
long?startTime?=?VERBOSE_IS_LOGGABLE???LogTime.getLogTime()?:?0;
EngineKey?key?=
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource>?memoryResource;
synchronized?(this)?{
memoryResource?=?loadFromMemory(key,?isMemoryCacheable,?startTime);
if?(memoryResource?==?null)?{
return?waitForExistingOrStartNewJob(...);
}
}
//?Avoid?calling?back?while?holding?the?engine?lock,?doing?so?makes?it?easier?for?callers?to
//?deadlock.
cb.onResourceReady(
memoryResource,?DataSource.MEMORY_CACHE,?/*?isLoadedFromAlternateCacheKey=?*/?false);
return?null;
}
3.EngineKey
An in memory only cache key used to multiplex loads.
用于多路傳輸加載的僅內存緩存密鑰.
EngineKey?key?=
keyFactory.buildKey(
...);
4.loadFromMemory
根據上面load方法提供咱們來看看loadFromMemory()這個是重點;
5.loadFromActiveResources
6.loadFromCache
7.getEngineResourceFromCache
到這里如有還未找到,那就說明該圖片未保存至內存緩存中來。咱繼續往下走,順著源碼跑。
8.waitForExistingOrStartNewJob
咱弄個簡化版
private?
//通過添加和刪除加載的回調并通知來管理加載的類
//加載完成時回調。
//咱都沒數據肯定沒加載完成,這個不管。急著往下看
EngineJob>?current?=?jobs.get(key,?onlyRetrieveFromCache);
if?(current?!=?null)?{
current.addCallback(cb,?callbackExecutor);
if?(VERBOSE_IS_LOGGABLE)?{
logWithTimeAndKey("Added?to?existing?load",?startTime,?key);
}
return?new?LoadStatus(cb,?current);
}
//同上,接著向下看
EngineJob
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
//負責從緩存數據或原始源解碼資源的類,看著像,咱看看DecodeJob
//應用轉換和代碼轉換。
DecodeJob
decodeJobFactory.build(
...
engineJob);
jobs.put(key,?engineJob);
engineJob.addCallback(cb,?callbackExecutor);
engineJob.start(decodeJob);
if?(VERBOSE_IS_LOGGABLE)?{
logWithTimeAndKey("Started?new?load",?startTime,?key);
}
return?new?LoadStatus(cb,?engineJob);
}
9.DecodeJob
class?DecodeJob
implements?DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable
Poolable?{
}
...
//構造方法有個DiskCacheProvider看著跟磁盤緩存有關咱進去瞅瞅
DecodeJob(DiskCacheProvider?diskCacheProvider,?Pools.Pool
this.diskCacheProvider?=?diskCacheProvider;
this.pool?=?pool;
}
...
10.DiskCacheProvider
磁盤緩存實現的入口。
在指定的內存中創建基于{@link com.bumptech.glide.disklrucache.disklrucache}的磁盤緩存。
磁盤緩存目錄。
public?class?DiskLruCacheFactory?implements?DiskCache.Factory?{
private?final?long?diskCacheSize;
private?final?CacheDirectoryGetter?cacheDirectoryGetter;
/**?在UI線程外調用接口以獲取緩存文件夾。?*/
public?interface?CacheDirectoryGetter?{
File?getCacheDirectory();
}
public?DiskLruCacheFactory(final?String?diskCacheFolder,?long?diskCacheSize)?{
this(
new?CacheDirectoryGetter()?{
@Override
public?File?getCacheDirectory()?{
return?new?File(diskCacheFolder);
}
},
diskCacheSize);
}
public?DiskLruCacheFactory(
final?String?diskCacheFolder,?final?String?diskCacheName,?long?diskCacheSize)?{
this(
new?CacheDirectoryGetter()?{
@Override
public?File?getCacheDirectory()?{
return?new?File(diskCacheFolder,?diskCacheName);
}
},
diskCacheSize);
}
/**
*使用此構造函數時,將調用{@link?CacheDirectoryGetter#getCacheDirectory()}
*UI線程,允許在不影響性能的情況下進行I/O訪問。
*在UI線程外調用@param?cacheDirectoryGetter接口以獲取緩存文件夾。
*@param?diskCacheSize?LRU磁盤緩存所需的最大字節大小。
*/
//?Public?API.
@SuppressWarnings("WeakerAccess")
public?DiskLruCacheFactory(CacheDirectoryGetter?cacheDirectoryGetter,?long?diskCacheSize)?{
this.diskCacheSize?=?diskCacheSize;
this.cacheDirectoryGetter?=?cacheDirectoryGetter;
}
@Override
public?DiskCache?build()?{
File?cacheDir?=?cacheDirectoryGetter.getCacheDirectory();
if?(cacheDir?==?null)?{
return?null;
}
if?(cacheDir.isDirectory()?||?cacheDir.mkdirs())?{
return?DiskLruCacheWrapper.create(cacheDir,?diskCacheSize);
}
return?null;
}
}
11.DiskCache.Factory
DiskLruCacheFactory實現的接口是什么,咱看看
/**?用于向磁盤緩存寫入數據和從磁盤緩存讀取數據的接口?*/
public?interface?DiskCache?{
/**?用于創建磁盤緩存的接口?*/
interface?Factory?{
/**?250?MB?of?cache.?*/
int?DEFAULT_DISK_CACHE_SIZE?=?250?*?1024?*?1024;
String?DEFAULT_DISK_CACHE_DIR?=?"image_manager_disk_cache";
/**?返回新的磁盤緩存,如果無法創建磁盤緩存,則返回{@code?null}*/
@Nullable
DiskCache?build();
}
/**?向磁盤緩存中的密鑰實際寫入數據的接口?*/
interface?Writer?{
/**
*將數據寫入文件
*如果寫入操作應中止,則返回false。
*@param?file寫入程序應寫入的文件。
*/
boolean?write(@NonNull?File?file);
}
/**
*獲取給定鍵處的值的緩存。
*/
@Nullable
File?get(Key?key);
/**
*@param?key要寫入的密鑰。
*@param?writer一個接口,該接口將在給定密鑰輸出流的情況下寫入數據。
*/
void?put(Key?key,?Writer?writer);
/**
*?從緩存中刪除鍵和值。.
*/
@SuppressWarnings("unused")
void?delete(Key?key);
/**?Clear?the?cache.?*/
void?clear();
}
磁盤緩存寫入和讀取的接口有了,那其他相關聯的源碼找到試著理解也是沒問題的,再多找就亂了。
LRU是什么
LRU是近期最少使用的算法(緩存淘汰算法),它的核心思想是當緩存滿時,會優先淘汰那些近期最少使用的緩存對象。采用LRU算法的緩存有兩種:LrhCache和DisLruCache,分別用于實現內存緩存和硬盤緩存,其核心思想都是LRU緩存算法。
LruCache的核心思想很好理解,就是要維護一個緩存對象列表,其中對象列表的排列方式是按照訪問順序實現的,即一直沒訪問的對象,將放在隊尾,即將被淘汰。而最近訪問的對象將放在隊頭,最后被淘汰。
內存緩存的LRU
/**?An?LRU?in?memory?cache?for?{@link?com.bumptech.glide.load.engine.Resource}s.?*/
public?class?LruResourceCache?extends?LruCache
private?ResourceRemovedListener?listener;
/**
*LruResourceCache的構造函數。
*@param?size內存緩存可以使用的最大字節大小。
*/
public?LruResourceCache(long?size)?{
super(size);
}
@Override
public?void?setResourceRemovedListener(@NonNull?ResourceRemovedListener?listener)?{
this.listener?=?listener;
}
@Override
protected?void?onItemEvicted(@NonNull?Key?key,?@Nullable?Resource>?item)?{
if?(listener?!=?null?&&?item?!=?null)?{
listener.onResourceRemoved(item);
}
}
@Override
protected?int?getSize(@Nullable?Resource>?item)?{
if?(item?==?null)?{
return?super.getSize(null);
}?else?{
return?item.getSize();
}
}
@SuppressLint("InlinedApi")
@Override
public?void?trimMemory(int?level)?{
if?(level?>=?android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND)?{
//正在輸入緩存的后臺應用程序列表
//退出我們的整個Bitmap緩存
clearMemory();
}?else?if?(level?>=?android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
||?level?==?android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL)?{
//?The?app's?UI?is?no?longer?visible,?or?app?is?in?the?foreground?but?system?is?running
//?critically?low?on?memory
//?Evict?oldest?half?of?our?bitmap?cache
trimToSize(getMaxSize()?/?2);
}
}
}
LruCache
存在一個LinkedHashMap存放數據,并且實現了LRU(最少使用算法)緩存策略。
Map
其中第二個參數0.75f表示加載因子,即容量達到75%的時候會把內存臨時增加一倍。
最后這個參數也至關重要,表示訪問元素的排序方式,true表示按照訪問順序排序,false表示按敗插入的順序排序。
LruCache實現原理
利用了LinkedHashMap排序方式的特性:由于使用訪問順序排序,進行了get/put操作的元素會放在Map最后面。所以當最后一個元素插入進來時,如果當前的緩存數據大小超過了最大限制,那么會刪除Map中放在前面的元素。
往期回顧
RecyclerView 繪制流程及Recycler緩存
Java的四種引用方式
Glide 的簡單使用
Android
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。