主流開源框架源碼深刻了解第3篇——Glide源碼分析。(源碼以4.9.0版爲準)html
本文從如下幾個方向進行分析:java
添加Gradle依賴:android
api 'com.github.bumptech.glide:glide:4.9.0'
// glide默認加載網絡圖片使用的是HttpURLconnection
// 若替換成OkHttp則添加上 'com.github.bumptech.glide:okhttp3-integration:4.9.0' api 'com.github.bumptech.glide:okhttp3-integration:4.9.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'git
Glide的使用github
// 基本使用:
Glide.with(context)
.load(iconUrl)
.apply(getOptions())
.into(imageView);
// 設置選項
private RequestOptions getOptions() {
return new RequestOptions()
.dontAnimate()
.placeholder(R.drawable.ic_no_picture)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.error(R.drawable.ic_no_picture);
}
// "注:若想實現自定義GlideModule,有兩種方式:"
1. 在AndroidManifest.xml Application標籤下添加
<meta-data
// 自定義GlideModule路徑
android:name="com.android.baselibrary.manager.glide.MyAppGlideModule"
android:value="GlideModule"/> // value爲固定值 GlideModule
2. 在自定義GlideModule實現類上添加註解@GlideModule
// 自定義GlideModule示例
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public boolean isManifestParsingEnabled() {
return false; // 告知Glide不須要再去 在AndroidManifest中查找是否有自定義GlideModule的實現類
}
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// 設置glide加載圖片格式 PREFER_RGB_565(glide4.0開始默認圖片加載格式爲PREFER_ARGB_8888)
builder.setDefaultRequestOptions(
new RequestOptions()
.format(DecodeFormat.PREFER_RGB_565)
);
//下面3中設置均可自定義大小,以及徹底自定義實現
//內存緩衝
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
.setMemoryCacheScreens(2)
.setBitmapPoolScreens(3)
.build();
builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));
//Bitmap 池
builder.setBitmapPool(new LruBitmapPool(calculator.getBitmapPoolSize()));
//磁盤緩存
int diskCacheSizeBytes = 1024 * 1024 * 100; //100 MB
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSizeBytes));
}
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
// registry.replace(GlideUrl.class, InputStream.class, new NetworkDisablingLoader.Factory());
}
}
複製代碼
先來一張時序圖:算法
with定義:Glide類中的靜態方法,具備多個重載方法設計模式
with做用:api
此部分涉及到的幾個重要的類:數組
源碼:緩存
public class Glide implements ComponentCallbacks2 {
......
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@Deprecated // 此方法已廢棄
@NonNull
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
......
}
複製代碼
從源碼中能夠看出,雖然with方法傳入的參數不一樣,最終調用的卻都是:getRetriever(view.getContext()).get(view),咱們先看getRetriever(view.getContext())實現:
// Glide類:
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// context不可爲空
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where " +
"getActivity() "
+ "returns null (which usually occurs when getActivity() is called before" +
" the Fragment "
+ "is attached or after the Fragment is destroyed).");
// 構建glide,並獲取RequestManagerRetriever對象
return Glide.get(context).getRequestManagerRetriever();
}
複製代碼
此方法先判斷context是否爲空,空則拋出異常。而後繼續調用Glide.get(context).getRequestManagerRetriever(),咱們依舊先看Glide.get(context):
// Glide類:
@NonNull
public static Glide get(@NonNull Context context) {
// 單例模式獲取glide實例
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
private static void checkAndInitializeGlide(@NonNull Context context) {
// 若正在初始化,則拋出異常,防止重複初始化
if (isInitializing) {
throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
+ " use the provided Glide instance instead");
}
// 正在初始化
isInitializing = true;
// 初始化glide
initializeGlide(context);
// 初始化完成
isInitializing = false;
}
private static void initializeGlide(@NonNull Context context) {
// 初始化GlideBuilder構造器
initializeGlide(context, new GlideBuilder());
}
@SuppressWarnings("deprecation")
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
// 獲取應用全局上下文
Context applicationContext = context.getApplicationContext();
// 獲取註解生成的自定義GlideModule(即:【Glide的基本用法】中自定義GlideModule方法2)
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
// 註解方式自定義GlideModule爲空;或者不爲空時,其函數isManifestParsingEnabled()返回true
// (通常狀況註解方式自定義GlideModule時,isManifestParsingEnabled方法應返回false,防止去繼續加載AndroidManifest.xml中的聲明,提升性能)
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
// 獲取應用AndroidManifest.xml中聲明的自定義GlideModule(即:【Glide的基本用法】中自定義GlideModule方法1)
manifestModules = new ManifestParser(applicationContext).parse();
}
// 若是有註解生成的Module就對比一下清單文件獲取的GlideModule,刪除相同的GlideModule
if (annotationGeneratedModule != null
&& !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
Set<Class<?>> excludedModuleClasses =
annotationGeneratedModule.getExcludedModuleClasses();
Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
while (iterator.hasNext()) {
com.bumptech.glide.module.GlideModule current = iterator.next();
if (!excludedModuleClasses.contains(current.getClass())) {
continue;
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
}
// 刪除相同的GlideModule
iterator.remove();
}
}
// 打印出從清單文件中獲取到的有效GlideModule
if (Log.isLoggable(TAG, Log.DEBUG)) {
for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
}
}
// 根據自定義的GlideModule獲取 RequestManagerFactory,若自定義GlideModule爲空,則返回null
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
// 設置factory
builder.setRequestManagerFactory(factory);
// 遍歷循環全部清單文件中獲取到的有效GlideModule,將全部GlideModule中設置的屬性,添加至builder中
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
// 將註解方式建立的GlideModule中自定義的屬性,添加到builder中
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
// 構建glide
Glide glide = builder.build(applicationContext);
/**
* 調用自定義GlideModule的registerComponents,並傳入當前的Glide實例來讓使用者註冊本身的組件,
* 其實在Glide實例化的過程當中已經註冊了默認的組件,若是用戶定義了相同的組件,那麼就會替換以前的。
*
* 註冊組件的目的就是告訴Glide,當咱們調用load(xxxx)方法時,應該用什麼方式來獲取這個xxxx所指向的資源。
* 所以,咱們能夠看到register的第一個參數就是咱們load(xxxx)的類型,第二個參數是對應的輸入流,而第三個參數就是定義獲取資源的方式。
* */
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.registerComponents(applicationContext, glide, glide.registry);
}
// 同上
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
/**
* 向Application 中註冊一個組件的回調, 用於檢測系統 Config 改變和內存佔用量低的信號
* Glide實現ComponentCallback2接口,關於ComponentCallback2:
* 是一個細粒度的內存回收管理回調。
* Application、Activity、Service、ContentProvider、Fragment實現了ComponentCallback2接口
* 開發者應該實現onTrimMemory(int)方法,細粒度release 內存,參數能夠體現不一樣程度的內存可用狀況
* 響應onTrimMemory回調:開發者的app會直接受益,有利於用戶體驗,系統更有可能讓app存活的更持久。
* 不響應onTrimMemory回調:系統更有可能kill 進程
* 關於ComponentCallback(ComponentCallbacks2 繼承自 ComponentCallbacks):
* 當組件在運行時,若是設備配置信息改變,系統就會回調onConfigurationChanged()。
* 當整個系統內存不足時調用onLowMemory (),正在運行的進程應該調整內存的佔用。
*
* */
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
@Nullable
@SuppressWarnings({"unchecked", "deprecation", "TryWithIdenticalCatches"})
private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules() {
GeneratedAppGlideModule result = null;
try {
// 反射獲取GeneratedAppGlideModuleImpl對象,
// 此類爲經過註解方式實現自定義GlideModule,在javac編譯程序時經過註解處理器(Annotation Processor)動態生成
Class<GeneratedAppGlideModule> clazz =
(Class<GeneratedAppGlideModule>)
Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");
result = clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
...
}
return result;
}
複製代碼
總結一下此部分的源碼:
咱們接着看一下Glide的構建builder.build(applicationContext):
public final class GlideBuilder {
/**
* 圖片變換處理的Map集合
* 圖片變化處理就是指圓角圖片之類的處理
*/
private final Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions =
new ArrayMap<>();
// 圖片加載引擎,負責啓動(發送網絡請求獲取圖片)並管理緩存資源活動
private Engine engine;
// Lru策略的圖片池,內存不足時自動清空
private BitmapPool bitmapPool;
// Lru策略的數組池,用於讀寫IO,內存不足時自動清空
private ArrayPool arrayPool;
// Lru策略的內存緩存池,內存不足時自動清空
private MemoryCache memoryCache;
// 線程池,用於查找內存緩存,最大線程數爲4,具體取決於cpu
private GlideExecutor sourceExecutor;
// 線程池,用於查找本地磁盤緩存,最大線程數爲4,具體取決於cpu
private GlideExecutor diskCacheExecutor;
/**
* 用於建立本地磁盤緩存對象的工廠
* 默認本地磁盤緩存size爲250M
*/
private DiskCache.Factory diskCacheFactory;
// 內存計算器,它經過獲取手機硬件常量和手機的屏幕密度、寬度和長度來計算出最適合當前狀況的內存緩存的大小
private MemorySizeCalculator memorySizeCalculator;
// 用於生產網絡狀態監聽事件的工廠
private ConnectivityMonitorFactory connectivityMonitorFactory;
// 設置Glide的log打印等級
private int logLevel = Log.INFO;
// Glide默認的配置選項
private RequestOptions defaultRequestOptions = new RequestOptions();
// 用於建立RequestManager對象的工廠
@Nullable
private RequestManagerFactory requestManagerFactory;
// 線程池,用於加載gif圖片,默認線程數爲1~2,最大數取決於cpu的核數
private GlideExecutor animationExecutor;
/**
* 是否將保留圖片資源數據不給回收,默認爲false
* 須要注意的是,設爲true將會致使更大的內存消耗,增長crash的概率
*/
private boolean isActiveResourceRetentionAllowed;
@Nullable
private List<RequestListener<Object>> defaultRequestListeners;
private boolean isLoggingRequestOriginsEnabled;
......
@NonNull
Glide build(@NonNull Context context) {
// -----------------初始化成員屬性 start-----------------
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
if (defaultRequestListeners == null) {
defaultRequestListeners = Collections.emptyList();
} else {
defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
}
// -----------------初始化成員屬性 end-----------------
// 初始化requestManagerRetriever對象
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
// 構建Glide
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock(),
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled);
}
}
// RequestManagerRetriever類:
public RequestManagerRetriever(@Nullable RequestManagerFactory factory) {
// 若factory爲null,則設置默認工廠
this.factory = factory != null ? factory : DEFAULT_FACTORY;
handler = new Handler(Looper.getMainLooper(), this /* Callback */);
}
private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
@NonNull
@Override
public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode requestManagerTreeNode,
@NonNull Context context) {
// 初始化RequestManager
return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
}
};
複製代碼
很明顯Glide構建過程初始化了不少屬性:
咱們再來看一看Glide的構造方法中作了些什麼:
// Glide類:
Glide(
@NonNull Context context,
@NonNull Engine engine,
@NonNull MemoryCache memoryCache,
@NonNull BitmapPool bitmapPool,
@NonNull ArrayPool arrayPool,
@NonNull RequestManagerRetriever requestManagerRetriever,
@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
@NonNull RequestOptions defaultRequestOptions,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
@NonNull List<RequestListener<Object>> defaultRequestListeners,
boolean isLoggingRequestOriginsEnabled) {
// 將 Builder 中的線程池, 緩存池等保存
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
// 解碼格式對象
DecodeFormat decodeFormat =
defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
final Resources resources = context.getResources();
registry = new Registry();
registry.register(new DefaultImageHeaderParser());
// 如今咱們只將這個解析器用於HEIF圖像,僅在OMR1 +上支持。
// 若是咱們須要這個用於其餘文件類型,咱們應該考慮刪除此限制。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
registry.register(new ExifInterfaceImageHeaderParser());
}
List<ImageHeaderParser> imageHeaderParsers = registry.getImageHeaderParsers();
Downsampler downsampler =
new Downsampler(
imageHeaderParsers,
resources.getDisplayMetrics(),
bitmapPool,
arrayPool);
// 解碼類:將InputStream中解碼成GIF
ByteBufferGifDecoder byteBufferGifDecoder =
new ByteBufferGifDecoder(context, imageHeaderParsers, bitmapPool, arrayPool);
// 解碼類:將Video中解碼成bitmap
ResourceDecoder<ParcelFileDescriptor, Bitmap> parcelFileDescriptorVideoDecoder =
VideoDecoder.parcel(bitmapPool);
// 解碼類:將ByteBuffer解碼成bitmap
ByteBufferBitmapDecoder byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
// 解碼類:將InputStreams解碼成bitmap
StreamBitmapDecoder streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
// 解碼類:經過Uri解碼成Drawable
ResourceDrawableDecoder resourceDrawableDecoder =
new ResourceDrawableDecoder(context);
// 解碼類:將資源文件轉換成InputStream
ResourceLoader.StreamFactory resourceLoaderStreamFactory =
new ResourceLoader.StreamFactory(resources);
// 將資源文件轉換成URI
ResourceLoader.UriFactory resourceLoaderUriFactory =
new ResourceLoader.UriFactory(resources);
// 將res中資源文件轉換成ParcelFileDescriptor
ResourceLoader.FileDescriptorFactory resourceLoaderFileDescriptorFactory =
new ResourceLoader.FileDescriptorFactory(resources);
// 將Asset中資源文件轉換成ParcelFileDescriptor
ResourceLoader.AssetFileDescriptorFactory resourceLoaderAssetFileDescriptorFactory =
new ResourceLoader.AssetFileDescriptorFactory(resources);
// Bitmap解碼類
BitmapEncoder bitmapEncoder = new BitmapEncoder(arrayPool);
// bitmap轉btye[]類
BitmapBytesTranscoder bitmapBytesTranscoder = new BitmapBytesTranscoder();
// GifDrawable轉btye[]類
GifDrawableBytesTranscoder gifDrawableBytesTranscoder = new GifDrawableBytesTranscoder();
ContentResolver contentResolver = context.getContentResolver();
// 使用 registry 註冊 Glide 須要的 Encoder 與 Decoder
registry
.append(ByteBuffer.class, new ByteBufferEncoder())
.append(InputStream.class, new StreamEncoder(arrayPool))
// 添加轉換成Bitmap相應解碼類
.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class,
byteBufferBitmapDecoder)
.append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class,
streamBitmapDecoder)
.append(
Registry.BUCKET_BITMAP,
ParcelFileDescriptor.class,
Bitmap.class,
parcelFileDescriptorVideoDecoder)
.append(
Registry.BUCKET_BITMAP,
AssetFileDescriptor.class,
Bitmap.class,
VideoDecoder.asset(bitmapPool))
.append(Bitmap.class, Bitmap.class, UnitModelLoader.Factory.<Bitmap>getInstance())
.append(
Registry.BUCKET_BITMAP, Bitmap.class, Bitmap.class, new UnitBitmapDecoder())
.append(Bitmap.class, bitmapEncoder)
// 添加轉換成BitmapDrawables相應解碼類
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
ByteBuffer.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
InputStream.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
ParcelFileDescriptor.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, parcelFileDescriptorVideoDecoder))
.append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder))
// 添加轉換成GIFs相應解碼類
.append(
Registry.BUCKET_GIF,
InputStream.class,
GifDrawable.class,
new StreamGifDecoder(imageHeaderParsers, byteBufferGifDecoder, arrayPool))
.append(Registry.BUCKET_GIF, ByteBuffer.class, GifDrawable.class,
byteBufferGifDecoder)
.append(GifDrawable.class, new GifDrawableEncoder())
/* GIF Frames */
// Compilation with Gradle requires the type to be specified for UnitModelLoader
// 添加將GIFs解碼成Bitmap
.append(
GifDecoder.class, GifDecoder.class,
UnitModelLoader.Factory.<GifDecoder>getInstance())
.append(
Registry.BUCKET_BITMAP,
GifDecoder.class,
Bitmap.class,
new GifFrameResourceDecoder(bitmapPool))
// 添加轉換成Drawables相應解碼類
.append(Uri.class, Drawable.class, resourceDrawableDecoder)
.append(
Uri.class, Bitmap.class,
new ResourceBitmapDecoder(resourceDrawableDecoder, bitmapPool))
// 添加文件處理類
.register(new ByteBufferRewinder.Factory())
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, File.class, new FileDecoder())
.append(File.class, ParcelFileDescriptor.class,
new FileLoader.FileDescriptorFactory())
// Compilation with Gradle requires the type to be specified for UnitModelLoader
// here.
.append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
// 添加轉換類(將任意複雜的數據模型轉化爲一個具體的數據類型,而後經過DataFetcher處理獲得相應的可用資源)
.register(new InputStreamRewinder.Factory(arrayPool))
// 經過資源文件轉化成InputStream
.append(int.class, InputStream.class, resourceLoaderStreamFactory)
// 經過資源文件轉化成ParcelFileDescriptor
.append(
int.class,
ParcelFileDescriptor.class,
resourceLoaderFileDescriptorFactory)
// 經過資源文件轉化成Uri
.append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
.append(
Integer.class,
ParcelFileDescriptor.class,
resourceLoaderFileDescriptorFactory)
.append(Integer.class, Uri.class, resourceLoaderUriFactory)
.append(
int.class,
AssetFileDescriptor.class,
resourceLoaderAssetFileDescriptorFactory)
.append(
Integer.class,
AssetFileDescriptor.class,
resourceLoaderAssetFileDescriptorFactory)
.append(int.class, Uri.class, resourceLoaderUriFactory)
// 經過字符串轉化成InputStream
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
// 經過Uri轉化成InputStream
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
// 經過String轉化成InputStream
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
// 經過String轉化成ParcelFileDescriptor
.append(String.class, ParcelFileDescriptor.class,
new StringLoader.FileDescriptorFactory())
// 經過String轉化成AssetFileDescriptor
.append(
String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
// 經過網絡Uri轉化成InputStream
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
// 經過資產目錄Uri轉化成InputStream
.append(Uri.class, InputStream.class,
new AssetUriLoader.StreamFactory(context.getAssets()))
// 經過Uri轉化成ParcelFileDescriptor
.append(
Uri.class,
ParcelFileDescriptor.class,
new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
// 經過image Uri轉化成InputStream
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
// 經過video Uri轉化成InputStream
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
// 經過Uri轉化成InputStream
.append(
Uri.class,
InputStream.class,
new UriLoader.StreamFactory(contentResolver))
// 經過Uri轉化成ParcelFileDescriptor
.append(
Uri.class,
ParcelFileDescriptor.class,
new UriLoader.FileDescriptorFactory(contentResolver))
// 經過Uri轉化成AssetFileDescriptor
.append(
Uri.class,
AssetFileDescriptor.class,
new UriLoader.AssetFileDescriptorFactory(contentResolver))
// 經過http/https Uris轉化成InputStream
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
// 經過 java.net.URL轉化成InputStream
.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
// 經過多媒體文件uri轉化成文件
.append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
// 經過http/https url轉化成InputStream
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
// 經過數組轉化成ByteBuffer
.append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
// 經過數組轉化成InputStream
.append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
.append(Uri.class, Uri.class, UnitModelLoader.Factory.<Uri>getInstance())
.append(Drawable.class, Drawable.class,
UnitModelLoader.Factory.<Drawable>getInstance())
.append(Drawable.class, Drawable.class, new UnitDrawableDecoder())
//註冊轉碼類
//bitmap轉碼成BitmapDrawable
.register(
Bitmap.class,
BitmapDrawable.class,
new BitmapDrawableTranscoder(resources))
// bitmap轉碼成byte[]
.register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
// Drawable轉碼成byte[]
.register(
Drawable.class,
byte[].class,
new DrawableBytesTranscoder(
bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
// GifDrawable轉碼成byte[]
.register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
// 初始化ImageViewTargetFactory,負責生產正確的Target,into時用到
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
// 構建一個 Glide 的上下文
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptions,
defaultTransitionOptions,
defaultRequestListeners,
engine,
isLoggingRequestOriginsEnabled,
logLevel);
}
複製代碼
能夠看出Glide的初始化非常複雜,咱們來總結一下:
先簡單介紹兩個類:
GlideContext:glide的全局上下文,爲glide整個流程提供相應的組件(包括註冊器、加載引擎engine、imageViewTargetFactory目標工廠等)
DataFetcher(Glide的初始化中registry.append最後一個參數的父類)::Fetcher的意思是抓取,因此該類能夠稱爲數據抓取器。
做用:根據不一樣的數據來源(本地,網絡,Asset等)以及讀取方式(Stream,ByteBuffer等)來提取並解碼數據資源
其實現類以下:
終於Glide的初始化分析結束,咱們從新回到Glide.get(context).getRequestManagerRetriever()中的getRequestManagerRetriever()部分:
// Glide類:
@NonNull
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
複製代碼
實際上getRequestManagerRetriever()就是返回咱們在構建Glide過程當中(GlideBuilder.build()中)初始化的requestManagerRetriever。所以咱們最終回到getRetriever(context).get(context)中的get(context)部分:
// RequestManagerRetriever類:
// with參數爲Context調用此方法
@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)) {
// 若是在主線程而且context不是Application類型,則經過判斷context真實類型調用相應方法
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());
}
}
// 若不在主線程或者context爲ApplicationContext類型則調用此方法
return getApplicationManager(context);
}
// with參數類型爲 FragmentActivity調用此方法
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
// 後臺線程,按with參數類型爲applicationContext類型處理
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
// with參數類型爲Fragment調用此方法
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(fragment.getActivity(),
"You cannot start a load on a fragment before it is attached or after it is " +"destroyed");
// 後臺線程,按with參數類型爲applicationContext類型處理
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
}
}
// with參數類型爲Activity調用此方法
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
// 後臺線程,按with參數類型爲applicationContext類型處理
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
// with參數類型爲View調用此方法
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull View view) {
// 後臺線程,按with參數類型爲applicationContext類型處理
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
Preconditions.checkNotNull(view);
Preconditions.checkNotNull(view.getContext(),
"Unable to obtain a request manager for a view without a Context");
// 經過view的上下文獲取當前view處於的Activity
Activity activity = findActivity(view.getContext());
// 視圖可能位於其餘位置,例如服務,此時無activity,則視爲context爲.applicationContext類型
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
// 查找是否有v4包下的Fragment(經過android.R.id.content)
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);
}
// 查找是否有app(標準)包下的Fragment(經過android.R.id.content)
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
// 看成with參數類型爲Fragment處理
return get(fragment);
}
// with參數類型爲applicationContext類型調用此方法
@NonNull
private RequestManager getApplicationManager(@NonNull Context context) {
// 若不在主線程或者context爲ApplicationContext類型
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// 構建application級別的manager,並傳入ApplicationLifecycle
// 即此時context爲applicationContext類型,此時就意味着glide加載週期和application一致
// application啓動後便可加載圖片,知道application結束纔會去清除緩存等,中間即使內存不夠用或者activity/fragment不可見等glide也不會清除數據
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
複製代碼
get(context)方法有不少重載方法,分別根據傳入的參數不一樣有不一樣的處理,因爲若當前不在主線程或者context爲ApplicationContext類型時glide的週期回調基本沒有什麼意義,所以不具體看getApplicationManager方法,而其他方法中最終調用的就是兩個方法:
fragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible())
supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity))
複製代碼
兩個方法一個返回app包下的標準Fragment,一個返回v4包下的Fragment,兩個方法很類似,咱們就選擇supportFragmentGet方法看一下具體實現:
// RequestManagerRetriever類:
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
// 初始化自定義SupportRequestManagerFragment
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
// 獲取存入的requestManager,第一次爲空
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
// 構建requestManager,並將當前Fragment的lifecycle(ActivityFragmentLifecycle)傳入
requestManager =
factory.build(
glide, current.getGlideLifecycle(),
current.getRequestManagerTreeNode(), context);
// 將構建的requestManager設置到current中,用於除第一次之後直接獲取
current.setRequestManager(requestManager);
}
return requestManager;
}
@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint,
boolean isParentVisible) {
// 經過Tag查找是否存在Fragment,防止重複添加Fragment
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
// 從 pendingRequestManagerFragments 緩存中獲取一個
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
// 建立SupportRequestManagerFragment
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
// 將當前fragment添加進pendingSupportRequestManagerFragments緩存
pendingSupportRequestManagerFragments.put(fm, current);
// 將Fragment設置TAG並添加進當前activity
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
// 發送消息,清空剛存進去的Fragment(走到此步表明Fragment已添加進去,經過Tag判斷便可)
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
@Override
public boolean handleMessage(Message msg) {
boolean handled = true;
Object removed = null;
Object key = null;
switch (msg.what) {
case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
FragmentManager supportFm = (FragmentManager) msg.obj;
key = supportFm;
// 移除Fragment
removed = pendingSupportRequestManagerFragments.remove(supportFm);
break;
default:
handled = false;
break;
}
return handled;
}
// SupportRequestManagerFragment類:
public SupportRequestManagerFragment() {
初始化ActivityFragmentLifecycle
this(new ActivityFragmentLifecycle());
}
@VisibleForTesting
@SuppressLint("ValidFragment")
public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
// 賦值到成員屬性lifecycle中
this.lifecycle = lifecycle;
}
@NonNull // 將ActivityFragmentLifecycle返回
ActivityFragmentLifecycle getGlideLifecycle() {
return lifecycle;
}
複製代碼
如今咱們看完了getRetriever(context).get(context)中的get(context)部分的源碼,主要是在 Activity 頁面中添加一個RequestManagerFragment/SupportRequestManagerFragment 實例, 以便用於監聽 Activity 的生命週期, 而後給這個 Fragment 注入一個 RequestManager。而且爲了防止同一個Activity屢次重複建立Fragment,glide不只使用了Tag來標記,並且還使用了 pendingRequestManagerFragments 進行緩存,以保證不會重複建立Fragment,並且一旦 Fragment 順利生成後再又把它在 map 裏面清空。
(o゜▽゜)o☆[BINGO!]如今咱們已經看完此部分的全部源碼,讓咱們繼續吧!!!
load定義:Glide.with(T) 返回了RequestManager對象,因此接下來調用的是RequestManager.load(T)用來肯定加載源(URL字符串、圖片本地路徑等),因爲源有多種,所以load有多個重載方法。
load做用:預先建立好對圖片進行一系列操做(加載、編解碼、轉碼)的對象,並所有封裝到 RequestBuilder對象中。
RequestBuilder:一個通用類,能夠處理常規資源類型的設置選項和啓動負載。用於啓動下載並將下載的資源轉換爲指定的圖片資源對象,而後將圖片資源對象裝置在指定的控件上面,比方說外層傳入一個Drawable類型,就把下載的原始資源轉換爲Drawable的圖片資源對象。
目前可使用的原始資源轉換類型有如下:
load源碼
// RequestManager類:
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable byte[] model) {
return asDrawable().load(model);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
...
// 默認加載Drawable
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
// 資源類型爲Drawable.class
return as(Drawable.class);
}
// 指定加載Bitmap
public RequestBuilder<Bitmap> asBitmap() {
return as(Bitmap.class).apply(DECODE_TYPE_BITMAP);
}
// 指定加載Gif
public RequestBuilder<GifDrawable> asGif() {
return as(GifDrawable.class).apply(DECODE_TYPE_GIF);
}
@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
複製代碼
從多個load重載方法中能夠看出,都是先調用asDrawable()方法獲取一個RequestBuilder請求的構造器。從這裏咱們就知道了 Glide 默認將下載的圖片資源轉爲Drawable資源對象,若是對資源轉換有其餘指定的需求的話,咱們能夠在 laod 方法以前使用 asBitmap()或者 asGif() ,並且他們的內部也是同樣的代碼,只是傳入的Class不同而已。以後調用load方法並傳入參數。
// RequestBuilder類:
@NonNull
@Override
@CheckResult
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<TranscodeType> load(@Nullable Uri uri) {
return loadGeneric(uri);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<TranscodeType> load(@Nullable File file) {
return loadGeneric(file);
}
@NonNull
@CheckResult
@SuppressWarnings("unchecked")
@Override
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
return loadGeneric(model);
}
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
複製代碼
因爲load參數有多種類型,所以RequestBuilder的load方法也是有多個重載的,其最後調用的都是loadGeneric方法,此方法主要數據來源model保存到RequestBuilder自己的成員model裏;而且將isModelSet設置爲true,表示model已經設置過啦;最後返回對象自己this,以待後面的調用。
// RequestBuilder類:
@NonNull
@CheckResult
@Override
public RequestBuilder<TranscodeType> apply(@NonNull BaseRequestOptions<?> requestOptions) {
Preconditions.checkNotNull(requestOptions);
return super.apply(requestOptions);
}
// BaseRequestOptions類:
private static final int UNSET = -1;
private static final int SIZE_MULTIPLIER = 1 << 1;
private static final int DISK_CACHE_STRATEGY = 1 << 2;
private static final int PRIORITY = 1 << 3;
private static final int ERROR_PLACEHOLDER = 1 << 4;
private static final int ERROR_ID = 1 << 5;
private static final int PLACEHOLDER = 1 << 6;
private static final int PLACEHOLDER_ID = 1 << 7;
private static final int IS_CACHEABLE = 1 << 8;
private static final int OVERRIDE = 1 << 9;
private static final int SIGNATURE = 1 << 10;
private static final int TRANSFORMATION = 1 << 11;
private static final int RESOURCE_CLASS = 1 << 12;
private static final int FALLBACK = 1 << 13;
private static final int FALLBACK_ID = 1 << 14;
private static final int THEME = 1 << 15;
private static final int TRANSFORMATION_ALLOWED = 1 << 16;
private static final int TRANSFORMATION_REQUIRED = 1 << 17;
private static final int USE_UNLIMITED_SOURCE_GENERATORS_POOL = 1 << 18;
private static final int ONLY_RETRIEVE_FROM_CACHE = 1 << 19;
private static final int USE_ANIMATION_POOL = 1 << 20;
private static boolean isSet(int fields, int flag) {
return (fields & flag) != 0;
}
@NonNull
@CheckResult
public T apply(@NonNull BaseRequestOptions<?> o) {
if (isAutoCloneEnabled) {
return clone().apply(o);
}
BaseRequestOptions<?> other = o;
if (isSet(other.fields, SIZE_MULTIPLIER)) {
sizeMultiplier = other.sizeMultiplier;
}
if (isSet(other.fields, USE_UNLIMITED_SOURCE_GENERATORS_POOL)) {
useUnlimitedSourceGeneratorsPool = other.useUnlimitedSourceGeneratorsPool;
}
if (isSet(other.fields, USE_ANIMATION_POOL)) {
useAnimationPool = other.useAnimationPool;
}
if (isSet(other.fields, DISK_CACHE_STRATEGY)) {
diskCacheStrategy = other.diskCacheStrategy;
}
if (isSet(other.fields, PRIORITY)) {
priority = other.priority;
}
if (isSet(other.fields, ERROR_PLACEHOLDER)) {
errorPlaceholder = other.errorPlaceholder;
errorId = 0;
fields &= ~ERROR_ID;
}
if (isSet(other.fields, ERROR_ID)) {
errorId = other.errorId;
errorPlaceholder = null;
fields &= ~ERROR_PLACEHOLDER;
}
if (isSet(other.fields, PLACEHOLDER)) {
placeholderDrawable = other.placeholderDrawable;
placeholderId = 0;
fields &= ~PLACEHOLDER_ID;
}
if (isSet(other.fields, PLACEHOLDER_ID)) {
placeholderId = other.placeholderId;
placeholderDrawable = null;
fields &= ~PLACEHOLDER;
}
if (isSet(other.fields, IS_CACHEABLE)) {
isCacheable = other.isCacheable;
}
if (isSet(other.fields, OVERRIDE)) {
overrideWidth = other.overrideWidth;
overrideHeight = other.overrideHeight;
}
if (isSet(other.fields, SIGNATURE)) {
signature = other.signature;
}
if (isSet(other.fields, RESOURCE_CLASS)) {
resourceClass = other.resourceClass;
}
if (isSet(other.fields, FALLBACK)) {
fallbackDrawable = other.fallbackDrawable;
fallbackId = 0;
fields &= ~FALLBACK_ID;
}
if (isSet(other.fields, FALLBACK_ID)) {
fallbackId = other.fallbackId;
fallbackDrawable = null;
fields &= ~FALLBACK;
}
if (isSet(other.fields, THEME)) {
theme = other.theme;
}
if (isSet(other.fields, TRANSFORMATION_ALLOWED)) {
isTransformationAllowed = other.isTransformationAllowed;
}
if (isSet(other.fields, TRANSFORMATION_REQUIRED)) {
isTransformationRequired = other.isTransformationRequired;
}
if (isSet(other.fields, TRANSFORMATION)) {
transformations.putAll(other.transformations);
isScaleOnlyOrNoTransform = other.isScaleOnlyOrNoTransform;
}
if (isSet(other.fields, ONLY_RETRIEVE_FROM_CACHE)) {
onlyRetrieveFromCache = other.onlyRetrieveFromCache;
}
// Applying options with dontTransform() is expected to clear our transformations.
if (!isTransformationAllowed) {
transformations.clear();
fields &= ~TRANSFORMATION;
isTransformationRequired = false;
fields &= ~TRANSFORMATION_REQUIRED;
isScaleOnlyOrNoTransform = true;
}
fields |= other.fields;
// 將全部配置添加到Options對象中,此對象是直接在成員屬性中初始化
// 此對象內部維護一個CachedHashCodeArrayMap(ArrayMap的子類)類型的ArrayMap來存儲選項配置
options.putAll(other.options);
return selfOrThrowIfLocked();
}
複製代碼
RequestManager.apply(options)最後會調用到父類BaseRequestOptions的apply方法中。注意到BaseRequestOptions有一個int類型成員變量fields,apply方法一直在調用isSet()方法作判斷,isSet()邏輯是若是傳入的這個RequestOptions對象other設置了xxx屬性,就替換掉現有的屬性,若是特殊狀況再清楚掉其餘標誌位,最後會將全部配置添加到Options對象中。咱們來簡單說一聲fields,它是用來標誌各個屬性是否被賦值,咱們就拿經常使用的placeholder()方法來分析,placeholder()有兩個重載方法:
public RequestOptions placeholder(@Nullable Drawable drawable) {
if (isAutoCloneEnabled) {
return clone().placeholder(drawable);
}
this.placeholderDrawable = drawable;
fields |= PLACEHOLDER;//給表明drawable的placeholder標誌位至1
placeholderId = 0;
fields &= ~PLACEHOLDER_ID;//給表明id的placeholder標誌位至0
return selfOrThrowIfLocked();
}
public RequestOptions placeholder(@DrawableRes int resourceId) {
if (isAutoCloneEnabled) {
return clone().placeholder(resourceId);
}
this.placeholderId = resourceId;
fields |= PLACEHOLDER_ID;//給表明id的placeholder標誌位至1
placeholderDrawable = null;
fields &= ~PLACEHOLDER;//給表明drawable的placeholder標誌位至0
return selfOrThrowIfLocked();
}
複製代碼
placeholder(drawable)和placeholder(resourceId)這兩個方法不能同時對placeholder這一實物產生效果,因此會有fields |= PLACEHOLDER和fields &= ~PLACEHOLDER_ID這樣的代碼,
系統定義了21個標誌位,經過每一個標誌位表明RequestOptions對應屬性的賦值與否,巧妙使用位運算,用一個int類型表示了21個bool邏輯(其實一個int最多能標識32個邏輯)。
// RequestBuilder類:
@NonNull
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
// 注意:傳入Executors.mainThreadExecutor() 主線程調度器
return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// 根據View的scaleType,克隆一個RequestOptions,而後配置一個View的scaleType縮放選項
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
// 調用4個參數的into方法
return into(
// 調用 GlideContext.buildImageViewTarget 構建一個 ViewTarget
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
// 注意:傳入Executors.mainThreadExecutor() 主線程調度器
Executors.mainThreadExecutor());
}
@NonNull
@Synthetic
<Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
Executor callbackExecutor) {
return into(target, targetListener, /*options=*/ this, callbackExecutor);
}
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
......
}
複製代碼
into(target)方法也有多個重載,不過咱們仔細看,其實into只有兩個方法是提供給開發者使用的一個是into(@NonNull Y target)一個是into(@NonNull ImageView view),而咱們既能夠經過直接設置ImageView方法顯示圖片,也能夠經過設置自定義Target來實現資源回調的監聽,可在回調中設置圖片顯示。
此處咱們先引入一個概念Glide的Transformation變換(即:into(@NonNull ImageView view)中提到的配置View的scaleType縮放選項),而此概念放到後面【Glide之Transformation轉換】再進行分析。繼續咱們上面的into方法,在這幾個into的重載方法中,會發現其實最後調用的仍是最後一個4個參數的into方法:
// GlideContext類:
private final ImageViewTargetFactory imageViewTargetFactory;
public GlideContext(
...,
@NonNull ImageViewTargetFactory imageViewTargetFactory,
...) {
...
this.imageViewTargetFactory = imageViewTargetFactory;
...
}
@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
// 調用工廠類來建立一個 imageView 的 ViewTarget
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,@NonNull Class<Z> clazz) {
// 根據目標編碼的類型來建立不一樣的 ViewTarget 對象
// 由於咱們沒有調用asBitmap, 所以這裏爲 ViewTarget爲DrawableImageViewTarget
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode" + "(ResourceTranscoder)");
}
}
}
// RequestBuilder類:
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
// 調用 buildRequest 構建了一個 Glide 請求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
// 獲取當前ImageView對應的請求,初次爲null
Request previous = target.getRequest();
// 若當前ImageView對應的請求已經設置(request == previous)
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
// 若是上一次請求已完成,則再次開始將確保從新傳遞結果,觸發RequestListeners和Targets。
// 若是請求失敗,則再次開始從新啓動請求,再給它一次完成的機會。
// 若是請求已經正在運行,咱們可讓它繼續運行而不會中斷。
if (!Preconditions.checkNotNull(previous).isRunning()) {
// 使用先前的請求而不是新的請求來容許優化,
// 例如跳過設置佔位符,跟蹤和取消跟蹤目標,以及獲取視圖尺寸。
previous.begin();
}
return target;
}
// 清除先Target以前的全部任務並釋放資源
requestManager.clear(target);
// 給目標View設置請求(保存)
target.setRequest(request);
// 調用RequestManager.track 方法執行請求
requestManager.track(target, request);
return target;
}
複製代碼
能夠看到into(imageView)方法中使用GlideContext經過工廠類建立了ImageView的ViewTarget, 它描述的是圖像處理結束以後, 最終要做用到的 View目標,關於ViewTarget咱們放到後面【Glide之Target】部分分析。 構建好了ViewTarge, 就走到了最後一個into中,能夠看到調用了 buildRequest 構建了一個 Glide 的請求, 其構建過程也很是有意思, 最終最調用 SingleRequest.obtain 構建一個 Request 的實例對象, 以後即是調用 RequestManager.track 將其分發並執行。
咱們接着看requestManager.track(target, request)的實現:
// RequestManager類:
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
// targetTracker,在成員屬性時即初始化完成
// 做用:保存當前爲RequestManager激活的Target集,並轉發生命週期事件。
targetTracker.track(target);
// requestTracker,在RequestManager初始化時完成的初始化
// 做用:用於跟蹤,取消和從新啓動正在進行,已完成和失敗的請求的類。 此類不是線程安全的,必須在主線程上訪問。
requestTracker.runRequest(request);
}
// RequestTracker類:
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
複製代碼
可的看到,track方法是先將target添加到targetTracker類的targets集合中,而後運行request的begin方法。咱們直接點request.begin方法去跟蹤源碼,會發現Request只是一個接口,因此咱們要找到它的實現類。
咱們來看一下其Request的buildRequest構建請求的過程:
// RequestBuilder類:
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
// 構建請求 遞歸方法
return buildRequestRecursive(
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
private Request buildRequestRecursive(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
// 若有必要,首先構建ErrorRequestCoordinator,以便咱們能夠更新parentCoordinator。
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
// 主要要求
Request mainRequest =
// 構建縮略圖請求 遞歸方法
buildThumbnailRequestRecursive(
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
if (errorRequestCoordinator == null) {
return mainRequest;
}
// 構建錯誤請求
int errorOverrideWidth = errorBuilder.getOverrideWidth();
int errorOverrideHeight = errorBuilder.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !errorBuilder.isValidOverride()) {
errorOverrideWidth = requestOptions.getOverrideWidth();
errorOverrideHeight = requestOptions.getOverrideHeight();
}
Request errorRequest =
errorBuilder.buildRequestRecursive(
target,
targetListener,
errorRequestCoordinator,
errorBuilder.transitionOptions,
errorBuilder.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder,
callbackExecutor);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
private Request buildThumbnailRequestRecursive(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
// thumbnailBuilder默認爲null,除非手動調用
// RequestBuilder類的thumbnail(@Nullable RequestBuilder<TranscodeType> thumbnailRequest)方法。
if (thumbnailBuilder != null) {
// 遞歸案例:包含可能的遞歸縮略圖請求構建器。
if (isThumbnailBuilt) {
throw new IllegalStateException("You cannot use a request as both the main " +
"request and a "
+ "thumbnail, consider using clone() on the request(s) passed to " +
"thumbnail()");
}
TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions =
thumbnailBuilder.transitionOptions;
// 默認狀況下,將咱們的轉換應用於縮略圖請求,但避免覆蓋可能已明確應用於縮略圖請求的自定義選項
if (thumbnailBuilder.isDefaultTransitionOptionsSet) {
thumbTransitionOptions = transitionOptions;
}
// 縮略圖有線
Priority thumbPriority = thumbnailBuilder.isPrioritySet()
? thumbnailBuilder.getPriority() : getThumbnailPriority(priority);
// 縮略圖寬高
int thumbOverrideWidth = thumbnailBuilder.getOverrideWidth();
int thumbOverrideHeight = thumbnailBuilder.getOverrideHeight();
// 寬高檢驗
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !thumbnailBuilder.isValidOverride()) {
thumbOverrideWidth = requestOptions.getOverrideWidth();
thumbOverrideHeight = requestOptions.getOverrideHeight();
}
// 縮略圖請求協議器,同時協調原圖與縮略圖的request
ThumbnailRequestCoordinator coordinator =
new ThumbnailRequestCoordinator(parentCoordinator);
// 原圖請求
Request fullRequest =
obtainRequest(
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
isThumbnailBuilt = true;
//遞歸生成縮略圖請求
//調用buildRequestRecursive,仍是會調到buildThumbnailRequestRecursive,這是個遞歸方法
Request thumbRequest =
thumbnailBuilder.buildRequestRecursive(
target,
targetListener,
coordinator,
thumbTransitionOptions,
thumbPriority,
thumbOverrideWidth,
thumbOverrideHeight,
thumbnailBuilder,
callbackExecutor);
isThumbnailBuilt = false;
// 把這兩個request包裝到ThumbnailRequestCoordinator中
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
// thumbSizeMultiplier默認爲null,不然須要調用RequestBuilder類的thumbnail(float sizeMultiplier)方法
} else if (thumbSizeMultiplier != null) {
// 根據指定的縮放係數加載縮略圖
ThumbnailRequestCoordinator coordinator =
new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest =
obtainRequest(
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
BaseRequestOptions<?> thumbnailOptions =
requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);
Request thumbnailRequest =
obtainRequest(
target,
targetListener,
thumbnailOptions,
coordinator,
transitionOptions,
getThumbnailPriority(priority),
overrideWidth,
overrideHeight,
callbackExecutor);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// 只加載原圖
return obtainRequest(
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
}
}
private Request obtainRequest(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
return SingleRequest.obtain(
context,
glideContext,
model, // 對應load(url),好比一個圖片地址
transcodeClass,
requestOptions,
overrideWidth, // 寬
overrideHeight, // 高
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(), // 全局加載引擎
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
複製代碼
從源碼中來看,若咱們不調用RequestBuilder類的thumbnail()方法,最後構建的請求就是SingleRequest,那麼看看它的begin方法:
// SingleRequest類:
@Override
public synchronized void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
// 若是圖片的來源沒有設置,加載失敗
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
// 若是咱們在完成後從新啓動(一般經過notifyDataSetChanged 在相同的Target或View中啓動相同的請求),
// 咱們能夠簡單地使用上次檢索的資源和大小而且跳過獲取新的大小,開始新加載等。
// 這意味着想要從新啓動加載的用戶由於他們指望視圖大小已經改變而須要明確清除視圖或目標在開始新加載以前。
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// 從新啓動既不完整也不運行的請求能夠被視爲新的請求而且能夠從頭開始從新運行。
status = Status.WAITING_FOR_SIZE;
// 若是Target的寬高已經獲取併合法,就開始下一步
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
// 手動獲取寬高,注意:參數傳的是當前對象this,首次會走這裏
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
// 設置佔位圖
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
// ViewTarget類:
@CallSuper
@Override
public void getSize(@NonNull SizeReadyCallback cb) {
sizeDeterminer.getSize(cb);
}
// ViewTarget.SizeDeterminer類:
void getSize(@NonNull SizeReadyCallback cb) {
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
// 視圖狀態和大小有效
if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
// 又調用回了SingleRequest中的onSizeReady方法
cb.onSizeReady(currentWidth, currentHeight);
return;
}
// 咱們但願按照添加的順序通知回調,咱們只但願一次添加一個或多個回調,所以List是一個合理的選擇。
// 若在上一步獲取的寬高或者狀態無效,則先將cb保存在list集合中,以備後續測量好有效寬高後再調用onSizeReady方法
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
// 經過ViewTreeObserver監聽View繪製完成再獲取大小,防止獲取爲0
ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
// 添加預繪製偵聽器,此偵聽器,主要是監聽View在界面上繪製完成
// 而後調用其自身的onPreDraw()方法--》sizeDeterminer.checkCurrentDimens()
// --》notifyCbs(currentWidth, currentHeight) 來通知cbs集合按順序執行全部的onSizeReady方法
observer.addOnPreDrawListener(layoutListener);
}
}
複製代碼
SingleRequest實現了SizeReadyCallback接口用來回調當前View大小有效的測量完成。好,咱們來總結一下begin方法中的實現:
經過begin方法的總結,能夠明確的知道,接下來走到了SingleRequest中的onSizeReady方法:
// SingleRequest類:
@Override
public synchronized void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
// 計算縮略圖的尺寸
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
// 真正的加載流程
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
...
}
複製代碼
如今終於來到真正的加載流程啦!!!
public synchronized <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
// 根據傳入的參數, 構建這個請求的 引擎密鑰
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
// 從 內存ActiveResources(內部維護一個HashMap)緩存中查找這個 key 的引擎資源,嘗試複用
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
// 若引擎資源 存在, 無需繼續請求則直接回調 onResourceReady 處理後續操做
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
// 嘗試從 LruResourceCache 中找尋這個資源,嘗試複用,若存在則先緩存到內存ActiveResources中一份
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
// 若引擎資源 存在, 無需繼續請求則直接回調 onResourceReady 處理後續操做
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
// 從任務緩存中查找 key 對應的任務
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<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
// 構建解碼任務
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
// 添加到任務緩存中
jobs.put(key, engineJob);
// 添加到回調以及將callbackExecutor(into時初始化的主線程調度器)
engineJob.addCallback(cb, callbackExecutor);
// 執行任務
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
複製代碼
總結一下Engine.load中都幹了些什麼?:
構建這個請求的 key
從緩存中查找 key 對應的資源, 若存在直接調用onResourceReady表示資源已準備好
從任務緩存中查找 key 對應的任務
若存在則說明無需再次獲取資源
若3中不存在緩存任務,則構建新的任務
先了解幾個概念:
* 活動資源Active Resources 正在顯示的資源
* 內存緩存Memory cache 顯示過的資源
* 資源類型Resources 被解碼、轉換後的資源
* 數據來源Data 源文件(未處理過的資源)
複製代碼
接下來繼續看任務的執行engineJob.start(decodeJob):
// EngineJob類:
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
//這裏根據緩存策略,決定使用哪個Executor,默認狀況返回DiskCacheExecutor
//共有三種執行器,diskcacheExecutor,sourceExecutor,sourceUnlimitedExecutor
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
// 執行任務
executor.execute(decodeJob);
}
// DecodeJob類:
@SuppressWarnings("PMD.AvoidRethrowingException")
@Override
public void run() {
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
DataFetcher<?> localFetcher = currentFetcher;
try {
// 若是已經取消,則通知加載失敗
if (isCancelled) {
notifyFailed();
return;
}
// 調用了 runWrapped
runWrapped();
} catch (CallbackException e) {
......
}
}
private void runWrapped() {
switch (runReason) {
// 默認狀態爲INITISLIZE
case INITIALIZE:
// 獲取嘗試在哪裏解碼數據的枚舉類
stage = getNextStage(Stage.INITIALIZE);
// 獲取當前任務的DataFetcherGenerator(數據提取器生成器)
currentGenerator = getNextGenerator();
// 執行任務
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
// 獲取任務執行階段:初始化,讀取轉換後的緩存,讀取原文件(未處理)緩存,遠程圖片加載,結束狀態
private Stage getNextStage(Stage current) {
switch (current) {
// 初始狀態
case INITIALIZE:
// 若咱們配置的緩存策略容許從 資源緩存 中讀數據, 則返回 Stage.RESOURCE_CACHE
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
// 讀取轉換後的緩存
case RESOURCE_CACHE:
// 若咱們配置的緩存策略容許從 源數據緩存中讀數據, 則返回 Stage.DATA_CACHE
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
// 讀取原文件緩存
case DATA_CACHE:
// 若是用戶選擇僅從緩存中檢索資源,則從源跳過加載。
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE: // 遠程圖片加載
case FINISHED: // 結束狀態
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
private DataFetcherGenerator getNextGenerator() {
// 根據 嘗試在哪裏解碼數據的枚舉類 當前的類型獲取 數據提取器生成器
switch (stage) {
case RESOURCE_CACHE:
// 資源緩存生成器,從換後的緩存讀取文件
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
// 數據緩存生成器,從原文件緩存加載
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
// 源生成器,沒有緩存,遠程圖片資源加載器(好比網絡,本地文件)
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
// 這裏Generator.startNext方法中就是加載過程,若是成功就返回true並跳出循環,不然切換Generator繼續執行
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
// 從新獲取執行器
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
// 若是任務執行到去遠程加載,且切換任務執行環境
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// 這次請求失敗,繼續
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
@Override
public void reschedule() {
// 更改執行目標爲:SOURCE服務。固然也只有在stage == Stage.SOURCE的狀況下會被調用。
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this); // 這裏callback正是EngineJob。
}
//EngineJob類:代碼跟進EngineJob類中
@Override
public void reschedule(DecodeJob<?> job) {
// 能夠看到,這裏獲取的SourceExecutor來執行decodeJob。
//也就巧妙地將此decodeJob任務從cacheExecutor切換到了SourceExecutor,這樣分工協做更加高效。
getActiveSourceExecutor().execute(job);
}
複製代碼
能夠看到,這裏幾個方法構成了Glide的解碼流程:嘗試從轉換過的本地資源加載圖片;嘗試從沒有處理過的原始資源中加載圖片;嘗試遠程加載圖片。經過狀態的切換來尋找下一個加載器,直到加載到這張圖,返回成功,若是找不到,返回失敗。
再總結一下:
如今代碼已經走到了EngineJob類中的reschedule中,並執行了任務。
// SourceGenerator類:
/**
* SourceGenerator
* DataFetcher的簡介:Fetcher的意思是抓取,因此該類能夠稱爲數據抓取器
* 做用:根據不一樣的數據來源(本地,網絡,Asset等)以及讀取方式(Stream,ByteBuffer等)來提取並解碼數據資源
* 其實現類以下:
* AssetPathFetcher:加載Asset數據
* HttpUrlFetcher:加載網絡數據
* LocalUriFetcher:加載本地數據
* 其餘實現類...
*/
@Override
public boolean startNext() {
//1.判斷是否有緩存,若是有,直接加載緩存(這裏第一次加載,因此dataToCache爲null)
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
//是否有更多的ModelLoader
while (!started && hasNextModelLoader()) {
// 從 DecodeHelper 的數據加載集合中, 獲取一個數據加載器
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
// 選擇合適的LoadData,並使用fetcher來抓取數據
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
複製代碼
看到這裏是否有些蒙,ModelLoader是什麼?
如今咱們來回想一下,咱們上面Glide初始化時,是否有一段相似於這樣的代碼(很長很長的):
Glide(...) {
...
registry
...
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
....
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
...
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptions,
defaultTransitionOptions,
defaultRequestListeners,
engine,
isLoggingRequestOriginsEnabled,
logLevel);
}
複製代碼
而這段代碼中append的DataFetcher,都是很請求網絡相關的。咱們再來看一下registry這個註冊類:
public class Registry {
//各類功能類註冊器。加載、轉換、解碼、加密等。
private final ModelLoaderRegistry modelLoaderRegistry;
private final EncoderRegistry encoderRegistry;
private final ResourceDecoderRegistry decoderRegistry;
private final ResourceEncoderRegistry resourceEncoderRegistry;
private final DataRewinderRegistry dataRewinderRegistry;
private final TranscoderRegistry transcoderRegistry;
private final ImageHeaderParserRegistry imageHeaderParserRegistry;
...
//modelLoader註冊
public <Model, Data> Registry append(Class<Model> modelClass, Class<Data> dataClass,
ModelLoaderFactory<Model, Data> factory) {
modelLoaderRegistry.append(modelClass, dataClass, factory);
return this;
}
...
}
//繼續跟進代碼,ModelLoaderRegistry類中
public synchronized <Model, Data> void append(Class<Model> modelClass
, Class<Data> dataClass,ModelLoaderFactory<Model, Data> factory) {
multiModelLoaderFactory.append(modelClass, dataClass, factory);
cache.clear();
}
//最後進入MultiModelLoaderFactory類中的add方法
private <Model, Data> void add(Class<Model> modelClass, Class<Data> dataClass,
ModelLoaderFactory<Model, Data> factory, boolean append) {
Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);
//entries是一個list。因此,到這裏就知道註冊的LoaderFactory被緩存到了列表中,以便後面取用。
entries.add(append ? entries.size() : 0, entry);
}
}
複製代碼
經過以上代碼,知道了ModelLoaderFactory在Glide初始化時註冊到了一個列表中,以備後面使用。在分析DecodeJob的代碼裏時,咱們使用SourceGenerator加載遠程圖片,並分析到了loadData.fetcher.loadData(helper.getPriority(), this);是真正加載數據的地方。而獲取loadData的地方爲loadData = helper.getLoadData().get(loadDataListIndex++); 那helper是哪來的呢?helper其實是DecodeJob的成員屬性,而且直接在聲明時直接初始化啦,那麼咱們接着看helper.getLoadData()的實現:
//DecodeHelper
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
//根據model類型,經過glideContext中的registry註冊器獲取ModelLoader列表
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
// 構建LoadData
LoadData<?> current =
modelLoader.buildLoadData(model, width, height, options);
// 若current不爲空,則添加到loadData集合中
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
複製代碼
先說兩個概念:
能夠看出getLoadData()返回一個LoadData的集合。而跟網絡有關的數據抓取器:
.append(String.class, InputStream.class, new StringLoader.StreamFactory()
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
複製代碼
很明顯咱們基本傳入的都是String類型的url,所以咱們真實使用的是StringLoader:
public class StringLoader<Data> implements ModelLoader<String, Data> {
private final ModelLoader<Uri, Data> uriLoader;
// Public API.
@SuppressWarnings("WeakerAccess")
public StringLoader(ModelLoader<Uri, Data> uriLoader) {
this.uriLoader = uriLoader;
}
@Override
public LoadData<Data> buildLoadData(@NonNull String model, int width, int height,
@NonNull Options options) {
Uri uri = parseUri(model);
if (uri == null || !uriLoader.handles(uri)) {
return null;
}
return uriLoader.buildLoadData(uri, width, height, options);
}
...
/**
* Factory for loading {@link InputStream}s from Strings.
*/
public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
@NonNull
@Override
public ModelLoader<String, InputStream> build(
@NonNull MultiModelLoaderFactory multiFactory) {
//關鍵在這兒
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
}
複製代碼
明顯的能看出StringLoader.StreamFactory建立了StringLoader(實現ModelLoader接口),而在getLoadData()獲取列表時,調用的ModelLoader的buildLoadData方法來構建LoadData,而StringLoader的buildLoadData方法將經過Uri.class和InputStream.class建立一個ModelLoader給StringLoader,因此StringLoader的加載功能轉移了。並且根據註冊關係知道轉移到了HttpUriLoader中。而HttpUriLoader中也相似於StringLoader同樣,將加載功能轉移到了HttpGlideUrlLoader中,而HttpGlideUrlLoader.buildLoadData對應的LoadData的Fetcher就是HttpUrlFetcher。
// HttpUrlFetcher類:
/**
* HttpUrlFetcher的簡介:網絡數據抓取器,通俗的來說就是去服務器上下載圖片,支持地址重定向(最多5次)
*/
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
// 使用重定向加載數據,注意此處返回的是InputStream數據流
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null,
glideUrl.getHeaders());
// 加載完成,回調數據準備完畢
callback.onDataReady(result);
} catch (IOException e) {
...
}
}
private InputStream loadDataWithRedirects(URL url, int redirects
, URL lastUrl,Map<String,String> headers) throws IOException {
//重定向次數過多
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
//經過URL的equals方法來比較會致使網絡IO開銷,通常會有問題
//能夠參考 http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
//下面開始,終於看到了可愛的HttpUrlConnection下載圖片
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
urlConnection.setInstanceFollowRedirects(false);
urlConnection.connect();
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in
// addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
複製代碼
看到這,終於找到了網絡通信的代碼,就是經過HttpUrlConnection來獲取數據流InputStream並返回。固然也能夠自定義使用OkHttp。
接下來你也許會這樣(哈哈,我寫時一開始也這樣):
咱們從HttpUrlFetcher類中的loadData方法中能夠看到,當數據加載完成後會調用callback.onDataReady(result),那callback是誰呢?咱們是從SourceGenerator中開始加載的網絡數據,那callback理所應當是SourceGenerator,所以咱們看一下SourceGenerator的onDataReady(result)方法:
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
// 若數據不爲空,或者 若是此請求應緩存原始未修改數據,則返回true。
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
// 若是這段數據是緩存
dataToCache = data;
// 從新執行這次加載
cb.reschedule();
} else {
// 不是緩存,則走這一步
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
複製代碼
這裏的cb是誰呢?咱們是從DecodeJob初始化的SourceGenerator,所以cb就是咱們的DecodeJob啦,因爲咱們走的不是緩存而是網絡,所以咱們看一下DecodeJob的onDataFetcherReady方法:
// DecodeJob類:
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey; // 保存數據源的 key
this.currentData = data; // 保存數據源實體
this.currentFetcher = fetcher; // 保存數據源的獲取器
this.currentDataSource = dataSource; // 數據來源: url 爲 REMOTE 類型的枚舉, 表示從遠程獲取
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
// 若請求時線程和解析時線程不一致
runReason = RunReason.DECODE_DATA;
// 從新執行這次加載
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
// 調用 decodeFromRetrievedData 解析獲取的數據
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
private void decodeFromRetrievedData() {
...
Resource<R> resource = null;
try {
// 調用decodeFromData從數據中解碼出資源resource
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
// 通知編碼完成
notifyEncodeAndRelease(resource, currentDataSource);
} else {
// resource爲null,則從新加載
runGenerators();
}
}
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
// 從Fetcher中解碼
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
// 關閉流stream和鏈接urlConnection。
fetcher.cleanup();
}
}
@SuppressWarnings("unchecked")
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
// 獲取當前數據類的解析器 LoadPath
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
// 經過解析器來解析來解析數據
return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
LoadPath<Data, ResourceType, R> path) throws GlideException {
// 經過硬件配置獲取選項
Options options = getOptionsWithHardwareConfig(dataSource);
// 根據數據類型獲取一個數據重造器, 獲取的數據爲 InputStream, 所以它是一個 InputStreamRewinder 的實例
"注:DataRewinder做用爲處理將包裝的數據流重置到原始位置並返回。"
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// // 將解析資源的任務轉移到了 LoadPath.load 方法中
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
複製代碼
這段代碼中能夠看到方法由onDataFetcherReady--> decodeFromRetrievedData--> decodeFromData--> decodeFromFetcher--> runLoadPath--> path.load進入LoadPath.load 方法中去進行數據解析。而這個過程當中構建了一個LoadPath, 而後建立了一個InputStreamRewinder 類型的DataRewinder。
咱們先來看一下LoadPath怎麼來的?
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
@Nullable
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
LoadPath<Data, TResource, Transcode> result =
loadPathCache.get(dataClass, resourceClass, transcodeClass);
if (loadPathCache.isEmptyLoadPath(result)) {
return null;
} else if (result == null) { // 若是爲空
// 獲取DecodePath
"注:DecodePath相似於LoadData 來源於 Registry 註冊器,解析類"
List<DecodePath<Data, TResource, Transcode>> decodePaths =
getDecodePaths(dataClass, resourceClass, transcodeClass);
if (decodePaths.isEmpty()) {
result = null;
} else {
// 先建立一個LoadPaht
result =
new LoadPath<>(
dataClass, resourceClass, transcodeClass, decodePaths,
throwableListPool);
}
// put進緩存
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}
複製代碼
先從緩存中取LoadPath,若是爲null,先獲取一個DecodePath集合,咱們建立Glide的時候在registry中append的各類解析方式,getDecodePaths就是根據咱們傳入的參數拿到對應的解析類。在而後建立出LoadPath,傳入剛建立的DecodePath,並放入到緩存中。
接下來咱們進入LoadPath.load:
// LoadPath類:
public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options,
int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback,
throwables);
} finally {
listPool.release(throwables);
}
}
private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
@NonNull Options options,int width, int height,DecodePath.DecodeCallback<ResourceType> decodeCallback, List<Throwable> exceptions) throws GlideException {
Resource<Transcode> result = null;
// 嘗試遍歷全部decodePath來解碼
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
// 調用path.decode進行解碼
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
// 解碼完成則跳出循環
return result;
}
// DecodePath類:
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width
, int height,@NonNull Options options,DecodeCallback<ResourceType> callback) throws GlideException {
// 調用 decodeResource 將源數據(InputStream)解析成中間資源(Bitmap)
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
// 調用 DecodeCallback.onResourceDecoded 處理中間資源(處理成Drawable)
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
// 調用 ResourceTranscoder.transcode 將中間資源轉爲目標資源(對應的Target)
return transcoder.transcode(transformed, options);
}
@NonNull
private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder
, int width,int height, @NonNull Options options) throws GlideException {
List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
// 調用decodeResourceWithList
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
@NonNull
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder
,int width,int height, @NonNull Options options,List<Throwable> exceptions) throws GlideException {
Resource<ResourceType> result = null;
// 遍歷decoder集合,獲取到ResourceDecoder解碼器(包括BitmapDrawableDecoder,GifFrameResourceDecoder
// ,FileDecoder等),而後經過rewinder.rewindAndGet()獲取到InputStream數據流,而後調用decoder.decode方法
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
// 調用 ResourceDecoder.decode 解析源數據InputStream
result = decoder.decode(data, width, height, options);
}
} catch (IOException | RuntimeException | OutOfMemoryError e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to decode data for " + decoder, e);
}
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
複製代碼
經過上面代碼瞭解到,數據解析的任務最後是經過DecodePath來執行的, 它內部有三個操做:
咱們分別來看一下這三個操做:
因爲本次流程的源數據爲 InputStream 所以它的解析器爲 StreamBitmapDecoder:
@Override
public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
@NonNull Options options)
throws IOException {
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
// 用於檢索讀取時拋出的異常。
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
// 用於讀取數據。 確保咱們能夠在讀取圖像標題後始終重置,這樣即便標頭解碼失敗或溢出,咱們的讀取緩衝區,仍然能夠嘗試解碼整個圖像。
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
// 主要看這裏:根據請求配置的數據, 對數據流進行採樣壓縮, 獲取到一個 Resource<Bitmap>
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
複製代碼
能夠看到它內部經過 Downsampler.decode 方法對數據流進行採樣壓縮(採樣策略就是咱們在構建 Request時傳入的), 來獲取這個流的 Bitmap。
咱們看看獲取到了 Resource 以後, 如何處理這個資源:
從源碼中能夠看出資源處理調用的callback.onResourceDecoded(decoded)處理資源,而這個callback實際上就是咱們的DecodeJob.DecodeCallback這個回調類:
// DecodeJob.DecodeCallback類:
@NonNull
@Override
public Resource<Z> onResourceDecoded(@NonNull Resource<Z> decoded) {
// 調用了外部類的 onResourceDecoded 方法
return DecodeJob.this.onResourceDecoded(dataSource, decoded);
}
// DecodeJob類:
@Synthetic
@NonNull
<Z> Resource<Z> onResourceDecoded(DataSource dataSource,
@NonNull Resource<Z> decoded) {
// 獲取數據資源的類型
@SuppressWarnings("unchecked")
Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
// 若非從資源磁盤緩存中獲取的數據源, 則對資源進行 transformation 操做
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
// 根據類型獲取轉換器(例如:CenterCrop、FitCenter等)
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
// TODO: Make this the responsibility of the Transformation.
if (!decoded.equals(transformed)) {
decoded.recycle();
}
// 構建數據編碼的策略
final EncodeStrategy encodeStrategy;
final ResourceEncoder<Z> encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
encoder = decodeHelper.getResultEncoder(transformed);
encodeStrategy = encoder.getEncodeStrategy(options);
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
// 根據編碼策略, 構建緩存的 key
Resource<Z> result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
encodeStrategy)) {
if (encoder == null) {
throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
}
final Key key;
switch (encodeStrategy) {
case SOURCE:
// 源數據的 key
key = new DataCacheKey(currentSourceKey, signature);
break;
case TRANSFORMED:
key = new ResourceCacheKey(
decodeHelper.getArrayPool(),
currentSourceKey,
signature,
width,
height,
appliedTransformation,
resourceSubClass,
options);
break;
default:
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
// 初始化編碼管理者, 用於提交內存緩存
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
// 返回 transform 以後的 bitmap
return result;
}
複製代碼
onResourceDecoded方法中,其主要的邏輯是根據咱們設置的參數進行變化,也就是說,若是咱們使用了 centerCrop 等參數,那麼這裏將會對其進行處理。這裏的 Transformation 是一個接口,它的一系列的實現都是對應於 scaleType 等參數的,好比CenterCrop、FitCenter、CenterInside等都是實現了Transformation接口的縮放轉換器。
因爲咱們的目標格式爲Drawable, 所以它的轉換器爲 BitmapDrawableTranscoder,而ResourceTranscoder.transcode這三步實際上使用 BitmapDrawableTranscoder 的 transcode() 方法返回 Resouces。
這樣,當這三個方法也執行完畢,咱們的岔路口方法就分析完了。而後就是不斷向上 return 進行返回。因此,咱們又回到了 DecodeJob 的 decodeFromRetrievedData() 方法以下:
private void decodeFromRetrievedData() {
...
Resource<R> resource = null;
try {
// 調用decodeFromData從數據中解碼出資源resource
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
// 通知編碼完成
notifyEncodeAndRelease(resource, currentDataSource);
} else {
// resource爲null,則從新加載
runGenerators();
}
}
複製代碼
此時decodeFromData方法徹底走完,接下來就是notifyEncodeAndRelease:
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
......
// 通知已準備好
notifyComplete(result, dataSource);
......
try {
// 2. 將數據緩存到磁盤
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
...
}
}
private Callback<R> callback;
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
......
// 從DecodeJob的構建中, 咱們知道這個Callback是EngineJob
callback.onResourceReady(resource, dataSource);
}
複製代碼
此方法分兩步:1.通知資源已準備好 2. 將數據緩存到磁盤。咱們主要看一下notifyComplete方法中的callback.onResourceReady:
// EngineJob類:
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
......
engineResource = engineResourceFactory.build(resource, isCacheable);
// 在下面咱們的回調持續時間保持資源,因此咱們不會在通知中間回收它是否由其中一個回調同步釋放。
// 在鎖定下獲取它,以便在咱們調用回調以前,在下一個鎖定部分下面執行的任何新添加的回調都沒法回收資源。
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
// 1. 通知上層 Engine 任務完成了
listener.onEngineJobComplete(this, localKey, localResource);
// 2. 回調給 ImageViewTarget 展現資源
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
}
// Engine類:
@Override
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
if (resource != null) {
resource.setResourceListener(key, this);
// 將加載好的資源添加到內存緩存
if (resource.isCacheable()) {
activeResources.activate(key, resource);
}
}
jobs.removeIfCurrent(key, engineJob);
}
private class CallResourceReady implements Runnable {
private final ResourceCallback cb;
CallResourceReady(ResourceCallback cb) {
this.cb = cb;
}
@Override
public void run() {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
engineResource.acquire();
// 呼叫資源就緒回撥
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
// EngineJob類:
@Synthetic
synchronized void callCallbackOnResourceReady(ResourceCallback cb) {
try {
// 回調SingleRequest中的onResourceReady方法
cb.onResourceReady(engineResource, dataSource);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
// SingleRequest類:
@Override
public synchronized void onResourceReady(Resource<?> resource, DataSource dataSource) {
......
// 資源就緒
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
...
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
// 注意這一步
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
複製代碼
這段代碼有點多有點亂,我一步一步說:
至此,終於Glide一次完整的請求顯示已經分析完成,不過這不過是Glide的最基礎的流程,而Glide 支持Gif, 視頻加載,二進制,Assets等操做, 可想而知其內部的處理了多少邏輯代碼,如此複雜的流程, 嵌套了如此之多的回調,想一想都很可怕,最後的解析資源部分大部分屬於借鑑,主要其內部實現實在是複雜。
整個流程走完啦,再根據時序圖回憶一下吧:
在說上面三個問題以前,咱們先來思考一下Glide對ImageView都有哪些行爲?
1. 獲取ImageView的大小
2. 根據生命週期調度ImageView是否從新加載、取消加載圖片
3. ImageView的動畫效果
4. ImageView加載過程當中顯示的默認圖片、加載失敗時顯示的圖片、加載成功時顯示的是Bitmap仍是Drawable以及動效效果
複製代碼
根據設計模式裏的單一職責原則,Request只處理圖片加載的邏輯;而Target則是處理ImageView相關的相關行爲的接口,這就回答了第一個問題。
咱們來看下Target的主要相關的類圖關係:
a、獲取一個View的大小無非是getWidth()、layoutParam.width,而當View還沒繪製時是拿不到大小的,
那麼此時經過Activity的onWindowFocusChanged或者ViewTreeObserver來監聽View的繪製完成時期
再調用getWidth就能夠拿到大小了。Glide的獲取是不太可能經過onWindowFocusChanged的了,
剩下就只剩下ViewTreeObserver了,所以Glide就是經過ViewTreeObserver來獲取的。
// ViewTarget類:
public void getSize(@NonNull SizeReadyCallback cb) {
//調用成員遍歷sizeDeterminer的getSize()方法
sizeDeterminer.getSize(cb);
}
// ViewTarget.SizeDeterminer類:
void getSize(@NonNull SizeReadyCallback cb) {
//獲取當前view的寬度
int currentWidth = getTargetWidth();
//獲取當前view的高度
int currentHeight = getTargetHeight();
if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
//若是View的大小大於0
//回調告知view的大小
cb.onSizeReady(currentWidth, currentHeight);
return;
}
if (!cbs.contains(cb)) {
//添加大小觀察者
cbs.add(cb);
}
if (layoutListener == null) {
//獲取ViwTreeObserver
ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
//監聽View的preDraw行爲
observer.addOnPreDrawListener(layoutListener);
}
}
//獲取寬度
private int getTargetWidth() {
int horizontalPadding = view.getPaddingLeft() + view.getPaddingRight();
LayoutParams layoutParams = view.getLayoutParams();
int layoutParamSize = layoutParams != null ? layoutParams.width : PENDING_SIZE;
return getTargetDimen(view.getWidth(), layoutParamSize, horizontalPadding);
}
//判斷寬高是否大於0
private boolean isViewStateAndSizeValid(int width, int height) {
return isDimensionValid(width) && isDimensionValid(height);
}
//判斷指定的大小是否大於0或者==Integer.MAX_VALUE
private boolean isDimensionValid(int size) {
return size > 0 || size == SIZE_ORIGINAL;
}
// ViewTarget.SizeDeterminerLayoutListener類:
public boolean onPreDraw() {
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
sizeDeterminer.checkCurrentDimens();
}
return true;
}
// ViewTarget.SizeDeterminer類:
void checkCurrentDimens() {
if (cbs.isEmpty()) {
return;
}
//獲取寬度
int currentWidth = getTargetWidth();
//獲取高度
int currentHeight = getTargetHeight();
if (!isViewStateAndSizeValid(currentWidth, currentHeight)) {
//若是寬高小於0,表示view還沒有測量好
return;
}
//回調通知監聽則view的大小已經測量好
notifyCbs(currentWidth, currentHeight);
//移除監聽者
clearCallbacksAndListener();
}
a 總結:先判斷當前View的大小是否大於0,若是大於0就直接回調onSizeReady告知View大小已知;
不然經過ViewTreeObserver監聽View的onPreDraw行爲來獲取View的大小並告知監聽者view的大小已經測量好。
b、監聽與Window的綁定關係,經過監聽view與window的綁定關係,進而調度圖片加載發起加載請求或者取消加載請求。
// ViewTarget類:
public final ViewTarget<T, Z> clearOnDetach() {
if (attachStateListener != null) {
return this;
}
//建立綁定狀態監聽者
attachStateListener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
//綁定到window
resumeMyRequest();
}
@Override
public void onViewDetachedFromWindow(View v) {
//從window解綁
pauseMyRequest();
}
};
maybeAddAttachStateListener();
return this;
}
//設置view綁定window狀態的監聽者
private void maybeAddAttachStateListener() {
if (attachStateListener == null || isAttachStateListenerAdded) {
return;
}
//添加綁定狀態監聽者
view.addOnAttachStateChangeListener(attachStateListener);
isAttachStateListenerAdded = true;
}
@Synthetic
void resumeMyRequest() {
//綁定window時
//獲取圖片加載對象request
Request request = getRequest();
if (request != null && request.isCleared()) {
//開始請求加載
request.begin();
}
}
@SuppressWarnings("WeakerAccess")
@Synthetic
void pauseMyRequest() {
//從window解綁時
//獲取圖片加載對象request
Request request = getRequest();
if (request != null) {
isClearedByUs = true;
//取消圖片加載
request.clear();
isClearedByUs = false;
}
}
複製代碼
// a、設置加載中的顯示圖片,將原來的圖片資源設置爲空,在設置placeHolder爲加載中顯示的圖片
public void onLoadStarted(@Nullable Drawable placeholder){
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
public void setDrawable(Drawable drawable){
view.setImageDrawable(drawable);
}
// b、設置加載失敗的顯示圖片,將原來的圖片資源設置爲空,在設置errorDrawable爲加載失敗顯示的圖片
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
setResourceInternal(null);
setDrawable(errorDrawable);
}
// c、圖片加載成功處理,在圖片加載成功回調時,若是沒有動效調用setResource設置加載成功的圖片資源,
// 而setResource是抽象方法,其實現是在DrawableImageViewTarget和BitmapImageVIewTarget來實現的;若是有動效則使用maybeUpdateAnimatable實現動效的邏輯。
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
//沒有動畫
//調用setResourceInternal設置加載成功的圖片
setResourceInternal(resource);
} else {
//有動畫,調用maybeUpdateAnimatable實現動效
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
//調用setResource設置圖片資源
setResource(resource);
//執行動效
maybeUpdateAnimatable(resource);
}
protected abstract void setResource(@Nullable Z resource);
// d、動畫的實現,先判斷圖片資源是不是Animatable的實現類,是的話就轉換爲Animatable,並調用start開始動效。
private void maybeUpdateAnimatable(@Nullable Z resource) {
if (resource instanceof Animatable) {
animatable = (Animatable) resource;
animatable.start();
} else {
animatable = null;
}
}
複製代碼
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
複製代碼
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
複製代碼
咱們在【4. into(T) (即:RequestBuilder.into(T))】中瞭解到into(imageview)方法中會根據當前的ImageView的scaleType(縮放類型)配置當前View的縮放選項。咱們以CenterCrop效果爲例,先看一個配置縮放效果:requestOptions.clone().optionalCenterCrop();
// BaseRequestOptions類:
// 此對象內部維護一個CachedHashCodeArrayMap(ArrayMap的子類)類型的ArrayMap來存儲選項配置(應用於內存和磁盤緩存鍵)
private Options options = new Options();
private Map<Class<?>, Transformation<?>> transformations = new CachedHashCodeArrayMap<>();
@NonNull
@CheckResult
public T optionalCenterCrop() {
// DownsampleStrategy 描述降採樣壓縮的策略
// 初始化CenterCrop對象,用來描述圖像變化方式
return optionalTransform(DownsampleStrategy.CENTER_OUTSIDE, new CenterCrop());
}
@SuppressWarnings({"WeakerAccess", "CheckResult"})
@NonNull
final T optionalTransform(@NonNull DownsampleStrategy downsampleStrategy,
@NonNull Transformation<Bitmap> transformation) {
if (isAutoCloneEnabled) {
return clone().optionalTransform(downsampleStrategy, transformation);
}
// 將降採樣壓縮策略添加到 options 中
downsample(downsampleStrategy);
// 將圖像變化方式添加到 transformations 中
return transform(transformation, /*isRequired=*/ false);
}
@NonNull
@CheckResult
public T downsample(@NonNull DownsampleStrategy strategy) {
return set(DownsampleStrategy.OPTION, Preconditions.checkNotNull(strategy));
}
@NonNull
@CheckResult
public <Y> T set(@NonNull Option<Y> option, @NonNull Y value) {
if (isAutoCloneEnabled) {
return clone().set(option, value);
}
Preconditions.checkNotNull(option);
Preconditions.checkNotNull(value);
options.set(option, value); // 保存到options中
return selfOrThrowIfLocked();
}
@NonNull
T transform(
@NonNull Transformation<Bitmap> transformation, boolean isRequired) {
if (isAutoCloneEnabled) {
return clone().transform(transformation, isRequired);
}
// DrawableTransformation Drawable轉換器
DrawableTransformation drawableTransformation =
new DrawableTransformation(transformation, isRequired);
// 調用了 transform 的重載方法, 將這個圖像變化的方式做用到多種資源類型上
transform(Bitmap.class, transformation, isRequired);
transform(Drawable.class, drawableTransformation, isRequired);
transform(BitmapDrawable.class, drawableTransformation.asBitmapDrawable(), isRequired);
transform(GifDrawable.class, new GifDrawableTransformation(transformation), isRequired);
return selfOrThrowIfLocked();
}
@NonNull
<Y> T transform(
@NonNull Class<Y> resourceClass,
@NonNull Transformation<Y> transformation,
boolean isRequired) {
if (isAutoCloneEnabled) {
return clone().transform(resourceClass, transformation, isRequired);
}
Preconditions.checkNotNull(resourceClass);
Preconditions.checkNotNull(transformation);
// 添加到了 transformations 緩存中
transformations.put(resourceClass, transformation);
...
return selfOrThrowIfLocked();
}
複製代碼
能夠看到配置縮放選項的操做除了添加了圖像變化操做, 還設定了採樣方式, 分別保存在 transformations 和 options 中。
這段代碼出現了幾個新的類:
Transformation用來幹什麼?
日常開發中,咱們時常會對網絡加載的圖片進行處理,
好比Glide自帶centerCrop(),fitCenter()處理,自定義圓形,圓角,模糊處理等等都是經過Transformation完成。
複製代碼
咱們先來了解一下CenterCrop類的源碼吧:
public class CenterCrop extends BitmapTransformation {
private static final String ID = "com.bumptech.glide.load.resource.bitmap.CenterCrop";
private static final byte[] ID_BYTES = ID.getBytes(CHARSET);
// 實現圖形變換,主要是這個方法
@Override
protected Bitmap transform(
@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
return TransformationUtils.centerCrop(pool, toTransform, outWidth, outHeight);
}
// 重寫epquals和hashcode方法,確保對象惟一性,以和其餘的圖片變換作區分
@Override
public boolean equals(Object o) {
return o instanceof CenterCrop;
}
@Override
public int hashCode() {
return ID.hashCode();
}
// 可經過內部算法 重寫此方法自定義圖片緩存key
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
messageDigest.update(ID_BYTES);
}
}
複製代碼
咱們能夠看到transform()的處理都在TransformationUtils中,那麼咱們看一下transform()方法的細節。
// TransformationUtils類:
public static Bitmap centerCrop(@NonNull BitmapPool pool, @NonNull Bitmap inBitmap, int width, int height) {
// 簡單校驗
if (inBitmap.getWidth() == width && inBitmap.getHeight() == height) {
return inBitmap;
}
// From ImageView/Bitmap.createScaledBitmap. 計算畫布的縮放的比例以及偏移值
final float scale;
final float dx;
final float dy;
Matrix m = new Matrix();
if (inBitmap.getWidth() * height > width * inBitmap.getHeight()) {
scale = (float) height / (float) inBitmap.getHeight();
dx = (width - inBitmap.getWidth() * scale) * 0.5f;
dy = 0;
} else {
scale = (float) width / (float) inBitmap.getWidth();
dx = 0;
dy = (height - inBitmap.getHeight() * scale) * 0.5f;
}
m.setScale(scale, scale);
m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
// 從Bitmap緩存池中嘗試獲取一個可重用的Bitmap對象
Bitmap result = pool.get(width, height, getNonNullConfig(inBitmap));
// 將原圖Bitmap對象的alpha值複製到裁剪Bitmap對象上面
TransformationUtils.setAlpha(inBitmap, result);
// 裁剪Bitmap對象進行繪製,並將最終的結果進行返回
applyMatrix(inBitmap, result, m);
return result;
}
複製代碼
對於equals,hashCode,updateDiskCacheKey不過重要,最重要的方法就是transform()。
若想要更多變換效果能夠嘗試自定義或者使用glide-transformations這個庫,它實現了不少通用的圖片變換效果,如裁剪變換、顏色變換、模糊變換等等,使得咱們能夠很是輕鬆地進行各類各樣的圖片變換。
此分析未包含Glide的緩存部分,感興趣的小夥伴先本身瞭解一下吧,實在是一入源碼深似海呀!
參考連接:
注:如有什麼地方闡述有誤,敬請指正。期待您的點贊哦!!!