Glide官方文檔地址
java
Glide.with(imageView.context)
.load(roundRectUrl)
.apply(RequestOptions().priority(Priority.HIGH))
.apply(RequestOptions().diskCacheStrategy(DiskCacheStrategy.DATA))
.apply(RequestOptions.centerCropTransform())
.apply(
RequestOptions.bitmapTransform(
RoundedCornersTransformation(
radius, 0, RoundedCornersTransformation.CornerType.ALL
)
)
)
.into(imageView)
複製代碼
Glide4.0以後,GlideModule推薦使用註解(@GlideModule)形式實現android
@GlideModule
class GlideConfigModule : AppGlideModule() {
override fun applyOptions(context: Context, builder: GlideBuilder) {
//設置內存大小
builder.setMemoryCache(LruResourceCache(1024 * 1024 * 100)) // 100M
//設置圖片緩存大小
builder.setBitmapPool(LruBitmapPool(1024 * 1024 * 50))
/**
* 設置磁盤緩存大小
*/
// 內部緩存目錄 data/data/packageName/DiskCacheName
builder.setDiskCache(
InternalCacheDiskCacheFactory(context, "GlideDemo", 1024 * 1024 * 100)
)
// 外部磁盤SD卡
/*builder.setDiskCache(
ExternalPreferredCacheDiskCacheFactory(context, "GlideDemo", 1024 * 1024 * 10)
)*/
}
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
// 替換網絡請求組件,使用OkHttp
val builder = OkHttpClient.Builder()
builder.addInterceptor(ProgressInterceptor)
val okHttpClient = builder.build()
registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(okHttpClient))
}
/**
* 禁用清單解析
*/
override fun isManifestParsingEnabled(): Boolean {
return false
}
}
複製代碼
當閱讀Glide源碼以前,咱們應該考慮一個問題,假設讓咱們本身去實現一個圖片加載框架,會須要哪些功能?
一、網絡組件,下載圖片 HttpUrlConnection OkHttp
二、請求隊列,線程池 優先級處理,請求的終止
三、緩存: 內存緩存,本地文件緩存,服務器緩存等等
四、內存資源回收機制: 先進先出,或者生命週期
五、更加基礎的組件就是各類圖片資源的轉碼解碼了
git
假設你去面試,面試官問你使用什麼圖片框架,你說你用Glide。面試官並非想讓你說Glide,而是你選擇Glide圖片加載框架的緣由和理由。它與其餘圖片加載框架的優缺點。
這裏引用前輩的文章,就不在單獨再寫一篇了:Android圖片加載框架Fresco,Glide,Picasso對比分析程序員
這是從網上找的流程圖: github
這裏就須要去閱讀個人上一篇文章了:Glide源碼分析One面試
OK,當你看完了3.2後,再看Glide的類關係圖你會至關的清晰,這個圖也是我從網上找的: 算法
接下來就是一些細節問題了
緩存
生命週期管理是在: 第一步Glide.with() 中建立的,具體邏輯在RequestManagerRetriever.get()中實現:
安全
public RequestManager get(@NonNull Context context) {
、、、
if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
複製代碼
get()方法重載了不少,參數對應與Glide.with()方法的參數,分別有Context、Activity、FragmentActivity、Fragment、android.support.v4.app.Fragment、View.
參數很重要,參數很重要,參數很重要!!!接下來給出解釋:
一、RequestManagerRetriever.get(Activity): 參數爲Activity的get()方法
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager(); // FragmentManager直接來自Activity
return fragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
複製代碼
二、RequestManagerRetriever.get(Fragment): 參數爲Fragment的get()方法
public RequestManager get(@NonNull Fragment fragment) {
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager(); // FragmentManager來自Fragment
return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
}
}
複製代碼
三、RequestManagerRetriever.get(View): 參數爲View的get()方法
public RequestManager get(@NonNull View view) {
...
Activity activity = findActivity(view.getContext());
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
// Support Fragments.
// Although the user might have non-support Fragments attached to FragmentActivity, searching
// for non-support Fragments is so expensive pre O and that should be rare enough that we
// prefer to just fall back to the Activity directly.
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);
}
// Standard Fragments.
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
複製代碼
經過View去尋找其所在的Fragment,若是該View在Fragment中,直接走參數爲Fragment的get(Fragment)方法;若是在Activity中,則直接走參數爲Activity的get(Activity)方法
重載的全部get()方法,最終都只有三種狀況:
1)、 getApplicationManager()
2)、 fragmentGet()
3)、 supportFragmentGet()
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or we are on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or
// activity. However, in this case since the manager attached to the application will not
// receive lifecycle events, we must force the manager to start resumed using
// ApplicationLifecycle.
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
複製代碼
看該方法的註釋,Request(也就是緩存)的生命週期是跟隨Application
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
// build RequestManager時,將RequestManagerFragment的生命週期(current.getGlideLifecycle())傳入
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
// 經過FragmentManager尋找是否已經添加過RequestManagerFragment,避免重複添加
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment(); // 新建一個空佈局的Fragment
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
// 在原有的界面上添加一個空佈局的Fragment,用於生命週期管理
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
複製代碼
Glide的請求的生命週期管理是經過向頁面添加一個空佈局的Fragment來實現的,上面說參數很重要,這裏給你們解惑。
一、當參數爲Activity時,空佈局的Fragment直接添加在Activity上,此時的Request的生命週期跟隨Activity;
二、當參數爲Fragment時,空佈局的Fragment直接添加在Fragment上,此時的Request的生命週期跟隨父Fragment;
三、單參數爲View時,這時會經過View去找尋該View是在Fragment上仍是在Activity中,分別走以上兩種狀況;
RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory,
Context context) {
···
// If we are the application level request manager, we may be created on a background thread.
// In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
// issue by delaying adding ourselves as a lifecycle listener by posting to the main thread.
// This should be entirely safe.
if (Util.isOnBackgroundThread()) {
mainHandler.post(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
···
}
private final Runnable addSelfToLifecycle = new Runnable() {
@Override
public void run() {
lifecycle.addListener(RequestManager.this);
}
};
複製代碼
一、mainHandler.post(addSelfToLifecycle);
當前線程爲後臺進程、或者是application等級的主線程時,經過mainHandler切回主線程,由於得刷新UI,主線程安全
二、lifecycle.addListener(this);
直接監聽上一步生成的佈局爲空的fragment的生命週期
public enum Priority {
IMMEDIATE,
HIGH,
NORMAL,
LOW,
}
複製代碼
前面分析Glide執行流程時發現,DecodeJob是真正幹活的類,其實現了Runnable接口,可是它還實現了Comparable<DecodeJob<?>>接口.
Engine實例化DecodeJob時,會將優先級屬性傳入DecodeJob中,具體使用在其實現的比較方法中:
/**
* @return a negative integer, zero, or a positive integer as
* this object is less than, equal to, or greater than the specified
**/
@Override
public int compareTo(@NonNull DecodeJob<?> other) {
int result = getPriority() - other.getPriority();
if (result == 0) {
result = order - other.order;
}
return result;
}
複製代碼
ThreadPoolExecutor:
execute()
PriorityBlockingQueue: // 線程池的任務隊列,選擇高優先級的任務優先執行
offer()
siftUpComparable()
同時實現Runnable和Comparable<DecodeJob<?>>接口,線程池自動實現優先級的自動排序。
在Glide初始化時,會指定一個默認的緩存算法,在GlideBuilder.build()方法中
Glide build(@NonNull Context context) {
···
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
···
}
複製代碼
使用了LRU(Least Recently Used)算法,核心就是最近最少使用。在算法的內部維護了一個LinkHashMap的鏈表(雙向鏈表),經過put數據的時候判斷是否內存已經滿了,若是滿了,則將最近最少使用的數據給剔除掉,從而達到內存不會爆滿的狀態。
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMapEntry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMapEntry<K,V> p =
(LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
複製代碼
緩存邏輯關鍵類Engine
public synchronized <R> LoadStatus load(...) {
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
...
}
複製代碼
model: Url、Uri、FilePath,圖片來源地址
這裏有個前提知識必須得知道:
DiskCacheStrategy.NONE 什麼都不緩存
DiskCacheStrategy.DATA 只緩存原來的全分辨率的圖像。
DiskCacheStrategy.RESOURCE 只緩存最終的圖像,即下降分辨率後的(或者是 轉換後的)
DiskCacheStrategy.ALL 緩存全部版本的圖像
DiskCacheStrategy.AUTOMATIC 讓Glide根據圖片資源智能地選擇使用哪種緩存策略 (默認選項)
width,height:從緩存的key中包含寬高能得出,同一張圖片,假設設置的不是緩存原始圖片,不一樣寬高將緩存不一樣的圖片。
優勢: 加載更快,少了圖片寬高處理的過程。
缺點: 更費內存
另外一Android圖片加載框架Picasso,只緩存一張全尺寸的圖片,優勢是佔用內存小,缺點是再次顯時示,須要從新調整大小。
Enginr.load():
public synchronized <R> LoadStatus load() {
...
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
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);
}
// 生成網絡請求邏輯
...
}
@Nullable
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire(); // 引用計數器加1
}
return active;
}
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key); // 將緩存數據從緩存隊列中移出
if (cached != null) {
cached.acquire(); // 引用計數器加1
activeResources.activate(key, cached); // 將數據放入正在活動的緩存列表中
}
return cached;
}
複製代碼
EngineResource.acquire():
synchronized void acquire() {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
++acquired; // 資源被引用的計數器
}
複製代碼
其實就是在圖片數據加載成功後,放入緩存列表。回調的最終方法在Engine的onEngineJobComplete()方法。
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null) {
resource.setResourceListener(key, this);
if (resource.isCacheable()) { // 判斷該資源是否須要緩存
activeResources.activate(key, resource); // 緩存
}
}
jobs.removeIfCurrent(key, engineJob);
}
複製代碼
Engine.onResourceReleased():
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey); // 從活動列表移除
if (resource.isCacheable()) {
cache.put(cacheKey, resource); // 放入緩存列表
} else {
resourceRecycler.recycle(resource);
}
}
複製代碼
移除緩存的具體調用流程:
這裏就得扯到前面的生命週期管理了,RequestManager中實現了LifecycleListener,接收Fragment或Activity的生命週期,這裏主要分析onDestory()方法:
RequestManager.onDestory():
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
複製代碼
RequestTracker.clearRequests() -> SingleRequest.clear() -> Engine.release() -> EngineResource.release()
void release() {
// To avoid deadlock, always acquire the listener lock before our lock so that the locking
// scheme is consistent (Engine -> EngineResource). Violating this order leads to deadlock
// (b/123646037).
synchronized (listener) {
synchronized (this) {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (--acquired == 0) { // 引用數值標準acquire減1
listener.onResourceReleased(key, this);
}
}
}
}
複製代碼
Glide源碼分析
Glide源碼分析流程思惟導圖 ©愛穿襯衫的程序員