Glide初始化,采用懶注冊(cè)的方式跺讯,設(shè)置參數(shù)
Glide是一個(gè)單例,應(yīng)用第一次使用Glide是會(huì)調(diào)用initializeGlide方法殉农,編譯期根據(jù)GlideModule或清單文件中的模塊命名(已棄用)生成GeneratedAppGlideModuleImpl類刀脏,提供初始化構(gòu)造參數(shù)的方法填充GlideBuilder,最終build出Glide
GlideModule注解類,提供初始化默認(rèn)參數(shù)修改超凳,AppGlideModule提供兩個(gè)方法
- isManifestParsingEnabled 是否讀取清單文件中的參數(shù)
- applyOptions 修改GlideBuilder的默認(rèn)初始化參數(shù)愈污,設(shè)置緩存參數(shù)之類
使用方法是新建類繼承這個(gè)類并且加上GlideModule注解,按照需要重寫上面兩個(gè)方法完成初始化參數(shù)設(shè)置
/**
Defines a set of dependencies and options to use when initializing Glide within an application.
...
*/
public abstract class AppGlideModule extends LibraryGlideModule implements AppliesOptions {
/**
* Returns {@code true} if Glide should check the AndroidManifest for {@link GlideModule}s.
*
* <p>Implementations should return {@code false} after they and their dependencies have migrated
* to Glide's annotation processor.
*
* <p>Returns {@code true} by default.
*/
public boolean isManifestParsingEnabled() {
return true;
}
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
// Default empty impl.
}
}
/**
* Registers a set of components to use when initializing Glide within an app when
*/
public abstract class LibraryGlideModule implements RegistersComponents {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
@NonNull Registry registry) {
// Default empty impl.
}
}
registerComponents可增加解析組件
例子:增加webp格式的圖片加載
@GlideModule
public class WebpGlideLibraryModule extends LibraryGlideModule {
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
// We should put our decoder before the build-in decoders,
// because the Downsampler will consume arbitrary data and make the inputstream corrupt
// on some devices
final Resources resources = context.getResources();
final BitmapPool bitmapPool = glide.getBitmapPool();
final ArrayPool arrayPool = glide.getArrayPool();
/* static webp decoders */
WebpDownsampler webpDownsampler = new WebpDownsampler(registry.getImageHeaderParsers(),
resources.getDisplayMetrics(), bitmapPool, arrayPool);
AnimatedWebpBitmapDecoder bitmapDecoder = new AnimatedWebpBitmapDecoder(arrayPool, bitmapPool);
ByteBufferBitmapWebpDecoder byteBufferBitmapDecoder = new ByteBufferBitmapWebpDecoder(webpDownsampler);
StreamBitmapWebpDecoder streamBitmapDecoder = new StreamBitmapWebpDecoder(webpDownsampler, arrayPool);
/* animate webp decoders */
ByteBufferWebpDecoder byteBufferWebpDecoder =
new ByteBufferWebpDecoder(context, arrayPool, bitmapPool);
registry
/* Bitmaps for static webp images */
.prepend(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
.prepend(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)
/* BitmapDrawables for static webp images */
.prepend(
Registry.BUCKET_BITMAP_DRAWABLE,
ByteBuffer.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
.prepend(
Registry.BUCKET_BITMAP_DRAWABLE,
InputStream.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
/* Bitmaps for animated webp images*/
.prepend(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class,
new ByteBufferAnimatedBitmapDecoder(bitmapDecoder))
.prepend(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class,
new StreamAnimatedBitmapDecoder(bitmapDecoder))
/* Animated webp images */
.prepend(ByteBuffer.class, WebpDrawable.class, byteBufferWebpDecoder)
.prepend(InputStream.class, WebpDrawable.class, new StreamWebpDecoder(byteBufferWebpDecoder, arrayPool))
.prepend(WebpDrawable.class, new WebpDrawableEncoder());
}
}
3級(jí)緩存結(jié)構(gòu)
緩存優(yōu)先級(jí) ActiveResources轮傍,MemoryCache暂雹,DiskCache
從最終的load方法可以看出,加載會(huì)先取ActiveResources创夜,再取MemoryCache如果有的話會(huì)從MemoryCache中刪除并且添加到ActiveResources中杭跪,最后創(chuàng)建EngineJob異步取本地緩存或網(wǎng)絡(luò)獲取,獲取成功后會(huì)添加到ActiveResources中驰吓,ActiveResources利用ReferenceQueue在弱應(yīng)用被回收時(shí)從ActiveResources中移除并添加到MemoryCache中涧尿。
public <R> LoadStatus load(...) {
...
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);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(...);
DecodeJob<R> decodeJob =
decodeJobFactory.build(...);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
- ActiveResources可以理解為是當(dāng)前正在被引用的資源弱引用(WeakReference)
- MemoryCache是內(nèi)存緩存,可設(shè)置大小限制檬贰,不會(huì)包含ActiveResources中的資源
- DiskCache 本地文件緩存姑廉,可設(shè)置緩存路徑及緩存文件總大小
- 這里有個(gè)細(xì)節(jié),為什么要有兩個(gè)內(nèi)存緩存翁涤,原因是MemoryCache是有大小限制的桥言,為防止正在使用的緩存被釋放,所以增加了ActiveResources
DiskCache本地緩存迷雪,首次獲取本地緩存時(shí)會(huì)觸發(fā)本地文件緩存掃描限书,緩存目錄會(huì)有一個(gè)journal文件
里面記錄了本地緩存文件列表及信息,如下
libcore.io.DiskLruCache
1
1
1CLEAN b31dfe4cd710e8868de840b27c2feba6443539b378343717c28174c5ff4c3a51 8318
CLEAN 3802507bffb423d3e9455801d15a5d82937e505f82134e0675e3096ecadbea5b 10414
CLEAN 28e5a4b66341ef88252f41602144354db0aa878c1863262e9950559df4488a1f 15396
private void readJournal() throws IOException {
StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII);
try {
String magic = reader.readLine();
String version = reader.readLine();
String appVersionString = reader.readLine();
String valueCountString = reader.readLine();
String blank = reader.readLine();
if (!MAGIC.equals(magic)
|| !VERSION_1.equals(version)
|| !Integer.toString(appVersion).equals(appVersionString)
|| !Integer.toString(valueCount).equals(valueCountString)
|| !"".equals(blank)) {
throw new IOException("unexpected journal header: [" + magic + ", " + version + ", "
+ valueCountString + ", " + blank + "]");
}
...
}
journal文件前5行目前來(lái)看只是起到了判斷文件是否是glide緩存目錄文件的一個(gè)規(guī)則,后面才是緩存文件信息章咧,以空格分開,分別是文件狀態(tài)/文件名/文件大小倦西,目前來(lái)看只有CLEAN狀態(tài)的文件才有文件大小信息
文件狀態(tài)有
private static final String CLEAN = "CLEAN"; //干凈的數(shù)據(jù)
private static final String DIRTY = "DIRTY"; //臟數(shù)據(jù),可能正在被修改
private static final String REMOVE = "REMOVE"; //已被刪除
private static final String READ = "READ"; //正在被讀取
文件名有后綴赁严,journal文件中文件名信息并不完整扰柠,可能考慮以后緩存多分文件所以是以name.0開始命名
緩存文件。
private Entry(String key) {
this.key = key;
this.lengths = new long[valueCount];
cleanFiles = new File[valueCount];
dirtyFiles = new File[valueCount];
// The names are repetitive so re-use the same builder to avoid allocations.
StringBuilder fileBuilder = new StringBuilder(key).append('.');
int truncateTo = fileBuilder.length();
for (int i = 0; i < valueCount; i++) {
fileBuilder.append(i);
cleanFiles[i] = new File(directory, fileBuilder.toString());
fileBuilder.append(".tmp");
dirtyFiles[i] = new File(directory, fileBuilder.toString());
fileBuilder.setLength(truncateTo);
}
}
Glide的生命周期
Glide通過(guò)with(context)的方法創(chuàng)建一個(gè)RequestManager疼约,這里要說(shuō)的就是這個(gè)manager的生命周期卤档,
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else 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);
}
如上代碼如果context是Application,則生命跟隨進(jìn)程程剥,若為Activity或FragmentActivity劝枣,則會(huì)創(chuàng)建一個(gè)空SupportRequestManagerFragment加到Activity中,監(jiān)聽activity的生命周期
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
由此即綁定了RequestManager與context的生命周期,發(fā)生變化后會(huì)通知給RequestManager中的Request和Target
@Override
public void onStart() {
resumeRequests();
targetTracker.onStart();
}
/**
* Lifecycle callback that unregisters for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
*/
@Override
public void onStop() {
pauseRequests();
targetTracker.onStop();
}
/**
* Lifecycle callback that cancels all in progress requests and clears and recycles resources for
* all completed requests.
*/
@Override
public 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);
}
以此實(shí)現(xiàn)請(qǐng)求的暫停/繼續(xù)與結(jié)束舔腾,該功能為我們實(shí)現(xiàn)滑動(dòng)時(shí)暫停圖片加載提供基礎(chǔ)幫助溪胶。