coil源碼流程分析
先看簡單使用
iv_image.load(
"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=80737749,1866961412&fm=26&gp=0.jpg"
) {
}
直接使用imageview 的拓展函數(shù)加載圖片疲吸,點進去:
@JvmSynthetic
inline fun ImageView.load(
uri: String?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable = loadAny(uri, imageLoader, builder)
@JvmSynthetic
inline fun ImageView.loadAny(
data: Any?,
imageLoader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {}
): Disposable {
val request = ImageRequest.Builder(context)
.data(data)
.target(this)
.apply(builder)
.build()
return imageLoader.enqueue(request)
}
最后走到loadAny 方法中前鹅,這里需要注意的是data為string 類型,tatget 為ImageViewTarget蹂喻,ImageViewTarget持有imageView的引用捂寿。
通過builder 的方式構(gòu)建一個request秦陋,用enqueue方法執(zhí)行,其中imageLoader為RealImageLoader對象
inline val Context.imageLoader: ImageLoader
@JvmName("imageLoader") get() = Coil.imageLoader(this)
@JvmStatic
fun imageLoader(context: Context): ImageLoader = imageLoader ?: newImageLoader(context)
@Synchronized
private fun newImageLoader(context: Context): ImageLoader {
// Check again in case imageLoader was just set.
imageLoader?.let { return it }
// Create a new ImageLoader.
val newImageLoader = imageLoaderFactory?.newImageLoader()
?: (context.applicationContext as? ImageLoaderFactory)?.newImageLoader()
?: ImageLoader(context)
imageLoaderFactory = null
imageLoader = newImageLoader
return newImageLoader
}
companion object {
/** Create a new [ImageLoader] without configuration. */
@JvmStatic
@JvmName("create")
operator fun invoke(context: Context) = Builder(context).build()
}
fun build(): ImageLoader {
//.....
return RealImageLoader(
context = applicationContext,
defaults = defaults,
bitmapPool = bitmapPool,
referenceCounter = referenceCounter,
strongMemoryCache = strongMemoryCache,
weakMemoryCache = weakMemoryCache,
callFactory = callFactory ?: buildDefaultCallFactory(),
eventListenerFactory = eventListenerFactory ?: EventListener.Factory.NONE,
componentRegistry = registry ?: ComponentRegistry(),
addLastModifiedToFileCacheKey = addLastModifiedToFileCacheKey,
launchInterceptorChainOnMainThread = launchInterceptorChainOnMainThread,
logger = logger
)
}
進去進入看RealImageLoader.enqueue 方法
override fun enqueue(request: ImageRequest): Disposable {
// Start executing the request on the main thread.
//這個應該是協(xié)程,不過是運行在主線程中的
val job = scope.launch {
//執(zhí)行
val result = executeMain(request, REQUEST_TYPE_ENQUEUE)
if (result is ErrorResult) throw result.throwable
}
// Update the current request attached to the view and return a new disposable.
return if (request.target is ViewTarget<*>) {
val requestId = request.target.view.requestManager.setCurrentRequestJob(job)
ViewTargetDisposable(requestId, request.target)
} else {
BaseTargetDisposable(job)
}
}
繼續(xù)往下探膊,看executeMain 方法
@MainThread
private suspend fun executeMain(initialRequest: ImageRequest, type: Int): ImageResult {
// Ensure this image loader isn't shutdown.
//判斷是否停止
check(!isShutdown.get()) { "The image loader is shutdown." }
// Apply this image loader's defaults to this request.
//創(chuàng)建一個新的request
val request = initialRequest.newBuilder().defaults(defaults).build()
// Create a new event listener.
//創(chuàng)建回調(diào)監(jiān)聽
val eventListener = eventListenerFactory.create(request)
// Wrap the target to support bitmap pooling.
//創(chuàng)建請求回調(diào)監(jiān)聽逞壁,用于得到請求結(jié)果時設(shè)置圖片
val targetDelegate = delegateService.createTargetDelegate(request.target, type, eventListener)
// Wrap the request to manage its lifecycle.
//創(chuàng)建lifecycle監(jiān)聽
val requestDelegate = delegateService.createRequestDelegate(request, targetDelegate, coroutineContext.job)
try {
// Fail before starting if data is null.
//檢查data
if (request.data == NullRequestData) throw NullRequestDataException()
// Enqueued requests suspend until the lifecycle is started.
//檢查生命周期
if (type == REQUEST_TYPE_ENQUEUE) request.lifecycle.awaitStarted()
// Set the placeholder on the target.
//設(shè)置占位圖
val cached = memoryCacheService[request.placeholderMemoryCacheKey]?.bitmap
try {
targetDelegate.metadata = null
// targetDelegate 就是 ImageViewTarget 對象锐锣, 調(diào)用onStart 方法雕憔,設(shè)置對應的占位圖
targetDelegate.start(cached?.toDrawable(request.context) ?: request.placeholder, cached)
eventListener.onStart(request)
request.listener?.onStart(request)
} finally {
referenceCounter.decrement(cached)
}
// Resolve the size.
//計算寬高
eventListener.resolveSizeStart(request)
val size = request.sizeResolver.size()
eventListener.resolveSizeEnd(request, size)
// Execute the interceptor chain.
//執(zhí)行,得到結(jié)果
val result = executeChain(request, type, size, cached, eventListener)
// Set the result on the target.
//判斷請求結(jié)果分瘦,設(shè)置成功失敗
when (result) {
is SuccessResult -> onSuccess(result, targetDelegate, eventListener)
is ErrorResult -> onError(result, targetDelegate, eventListener)
}
return result
} catch (throwable: Throwable) {
if (throwable is CancellationException) {
onCancel(request, eventListener)
throw throwable
} else {
// Create the default error result if there's an uncaught exception.
val result = requestService.errorResult(request, throwable)
onError(result, targetDelegate, eventListener)
return result
}
} finally {
requestDelegate.complete()
}
}
接下來走到executeChain方法中嘲玫,executeChain方法看著就像okhttp的責任鏈
private suspend inline fun executeChain(
request: ImageRequest,
type: Int,
size: Size,
cached: Bitmap?,
eventListener: EventListener
): ImageResult {
//很明顯,okhttp 方式的責任鏈抡诞,看對應的interceptors
val chain = RealInterceptorChain(request, type, interceptors, 0, request, size, cached, eventListener)
return if (launchInterceptorChainOnMainThread) {
chain.proceed(request)
} else {
withContext(request.dispatcher) {
chain.proceed(request)
}
}
}
很明顯土陪,就是使用okhttp的責任鏈方式鬼雀,請求攔截,其中interceptors的值為
//在 RealImageLoader 最頂部初始化取刃,先走registry中的interceptors璧疗,然后走EngineInterceptor
private val interceptors = registry.interceptors + EngineInterceptor(registry, bitmapPool, referenceCounter,
strongMemoryCache, memoryCacheService, requestService, systemCallbacks, drawableDecoder, logger)
走到EngineInterceptor 中的intercept 方法中
override suspend fun intercept(chain: Interceptor.Chain): ImageResult {
try {
// This interceptor uses some internal APIs.
check(chain is RealInterceptorChain)
//獲取對應的數(shù)據(jù)
val request = chain.request
val context = request.context
val data = request.data
val size = chain.size
val eventListener = chain.eventListener
// Perform any data mapping.
eventListener.mapStart(request, data)
//轉(zhuǎn)換,將string 類型的data轉(zhuǎn)換成Uri類型
val mappedData = registry.mapData(data)
eventListener.mapEnd(request, mappedData)
// Check the memory cache.
//獲取可以處理這次請求的fetcher 漆魔,這里只是獲取却音,沒有實際獲取
val fetcher = request.fetcher(mappedData) ?: registry.requireFetcher(mappedData)
//獲取內(nèi)存緩存的key
val memoryCacheKey = request.memoryCacheKey ?: computeMemoryCacheKey(request, mappedData, fetcher, size)
//判斷緩存策略系瓢,獲取緩存
val value = if (request.memoryCachePolicy.readEnabled) memoryCacheService[memoryCacheKey] else null
// Ignore the cached bitmap if it is hardware-backed and the request disallows hardware bitmaps.
//獲取緩存的bitmap
val cachedDrawable = value?.bitmap
?.takeIf { requestService.isConfigValidForHardware(request, it.safeConfig) }
?.toDrawable(context)
// Short circuit if the cached bitmap is valid.
//根據(jù)獲取的緩存,判斷緩存策略欠拾,如果有緩存骗绕,并且設(shè)置了緩存策略酬土,直接返回緩存
if (cachedDrawable != null && isCachedValueValid(memoryCacheKey, value, request, size)) {
return SuccessResult(
drawable = value.bitmap.toDrawable(context),
request = request,
metadata = Metadata(
memoryCacheKey = memoryCacheKey,
isSampled = value.isSampled,
dataSource = DataSource.MEMORY_CACHE,
isPlaceholderMemoryCacheKeyPresent = chain.cached != null
)
)
}
// Fetch, decode, transform, and cache the image on a background dispatcher.
//沒有緩存,協(xié)程切換io線程刹枉,開始請求
return withContext(request.dispatcher) {
// Mark the input data as ineligible for pooling (if necessary).
invalidateData(request.data)
// Decrement the value from the memory cache if it was not used.
if (value != null) referenceCounter.decrement(value.bitmap)
// Fetch and decode the image.
//執(zhí)行請求
val (drawable, isSampled, dataSource) =
execute(mappedData, fetcher, request, chain.requestType, size, eventListener)
// Mark the drawable's bitmap as eligible for pooling.
validateDrawable(drawable)
// Cache the result in the memory cache.
//寫入緩存
val isCached = writeToMemoryCache(request, memoryCacheKey, drawable, isSampled)
// Return the result.
//返回結(jié)果
SuccessResult(
drawable = drawable,
request = request,
metadata = Metadata(
memoryCacheKey = memoryCacheKey.takeIf { isCached },
isSampled = isSampled,
dataSource = dataSource,
isPlaceholderMemoryCacheKeyPresent = chain.cached != null
)
)
}
} catch (throwable: Throwable) {
if (throwable is CancellationException) {
throw throwable
} else {
return requestService.errorResult(chain.request, throwable)
}
}
}
根據(jù)上面分析嘶卧,走到execute方法中獲取圖片
private suspend inline fun execute(
data: Any,
fetcher: Fetcher<Any>,
request: ImageRequest,
type: Int,
size: Size,
eventListener: EventListener
): DrawableResult {
val options = requestService.options(request, size, systemCallbacks.isOnline)
//請求開始
eventListener.fetchStart(request, fetcher, options)
//使用fetcher請求對應的數(shù)據(jù)
val fetchResult = fetcher.fetch(bitmapPool, data, size, options)
eventListener.fetchEnd(request, fetcher, options, fetchResult)
val baseResult = when (fetchResult) {
is SourceResult -> {
val decodeResult = try {
// Check if we're cancelled.
coroutineContext.ensureActive()
// Find the relevant decoder.
val isDiskOnlyPreload = type == REQUEST_TYPE_ENQUEUE &&
request.target == null &&
!request.memoryCachePolicy.writeEnabled
val decoder = if (isDiskOnlyPreload) {
// Skip decoding the result if we are preloading the data and writing to the memory cache is
// disabled. Instead, we exhaust the source and return an empty result.
EmptyDecoder
} else {
//解碼請求的數(shù)據(jù)
request.decoder ?: registry.requireDecoder(request.data, fetchResult.source, fetchResult.mimeType)
}
// Decode the stream.
eventListener.decodeStart(request, decoder, options)
//解碼
val decodeResult = decoder.decode(bitmapPool, fetchResult.source, size, options)
eventListener.decodeEnd(request, decoder, options, decodeResult)
decodeResult
} catch (throwable: Throwable) {
// Only close the stream automatically if there is an uncaught exception.
// This allows custom decoders to continue to read the source after returning a drawable.
fetchResult.source.closeQuietly()
throw throwable
}
// Combine the fetch and decode operations' results.
DrawableResult(
drawable = decodeResult.drawable,
isSampled = decodeResult.isSampled,
dataSource = fetchResult.dataSource
)
}
is DrawableResult -> fetchResult
}
// Check if we're cancelled.
coroutineContext.ensureActive()
// Apply any transformations and prepare to draw.
//轉(zhuǎn)換得到的圖片,如圖片加圓角钟鸵、漸變涤躲、高斯模糊
val finalResult = applyTransformations(baseResult, request, size, options, eventListener)
(finalResult.drawable as? BitmapDrawable)?.bitmap?.prepareToDraw()
return finalResult
}
調(diào)用fetcher.fetch方法獲取數(shù)據(jù),其中fetcher是上面通過 registry.requireFetcher(mappedData) 來獲取蒙袍,這個很像Glide的方式
@Suppress("UNCHECKED_CAST")
internal fun <T : Any> ComponentRegistry.requireFetcher(data: T): Fetcher<T> {
val result = fetchers.findIndices { (fetcher, type) ->
type.isAssignableFrom(data::class.java) && (fetcher as Fetcher<Any>).handles(data)
}
checkNotNull(result) { "Unable to fetch data. No fetcher supports: $data" }
return result.first as Fetcher<T>
}
上面的方法嫩挤,就是循環(huán)fetchers,判斷對應的class類型和handles方法獲取對應的fetcher以现,其中fetchers的初始化在RealImageLoader中
private val registry = componentRegistry.newBuilder()
// Mappers
//添加轉(zhuǎn)換器约啊,上面講string 轉(zhuǎn)換成Uri,就是從這里獲取
.add(StringMapper())
.add(FileUriMapper())
.add(ResourceUriMapper(context))
.add(ResourceIntMapper(context))
// Fetchers
//添加Fetchers
.add(HttpUriFetcher(callFactory))
.add(HttpUrlFetcher(callFactory))
.add(FileFetcher(addLastModifiedToFileCacheKey))
.add(AssetUriFetcher(context))
.add(ContentUriFetcher(context))
.add(ResourceUriFetcher(context, drawableDecoder))
.add(DrawableFetcher(drawableDecoder))
.add(BitmapFetcher())
// Decoders
//添加Decoders
.add(BitmapFactoryDecoder(context))
.build()
通過循環(huán)獲取记盒,所以第一次獲取網(wǎng)絡(luò)的圖片是孽鸡,使用的是HttpUriFetcher
調(diào)用HttpUriFetcher 的fetch 方法
override suspend fun fetch(
pool: BitmapPool,
data: T,
size: Size,
options: Options
): FetchResult {
val url = data.toHttpUrl()
val request = Request.Builder().url(url).headers(options.headers)
val networkRead = options.networkCachePolicy.readEnabled
val diskRead = options.diskCachePolicy.readEnabled
//判斷緩存策略栏豺,通過okhttp設(shè)置網(wǎng)絡(luò)和本地緩存策略
when {
!networkRead && diskRead -> {
request.cacheControl(CacheControl.FORCE_CACHE)
}
networkRead && !diskRead -> if (options.diskCachePolicy.writeEnabled) {
request.cacheControl(CacheControl.FORCE_NETWORK)
} else {
request.cacheControl(CACHE_CONTROL_FORCE_NETWORK_NO_CACHE)
}
!networkRead && !diskRead -> {
// This causes the request to fail with a 504 Unsatisfiable Request.
request.cacheControl(CACHE_CONTROL_NO_NETWORK_NO_CACHE)
}
}
//請求得到結(jié)果
val response = callFactory.newCall(request.build()).await()
if (!response.isSuccessful) {
response.body()?.close()
throw HttpException(response)
}
val body = checkNotNull(response.body()) { "Null response body!" }
return SourceResult(
source = body.source(),
mimeType = getMimeType(url, body),
dataSource = if (response.cacheResponse() != null) DataSource.DISK else DataSource.NETWORK
)
}
這里設(shè)置了網(wǎng)絡(luò)奥洼、本地緩存,都是基于okhttp設(shè)置的嚼沿。得到了HTTP請求的數(shù)據(jù),通過decode進行解碼遣妥,其中decode為BitmapFactoryDecoder,decode方法主要是根據(jù)Source計算bitmap大小攀细,獲取對應的bitmap,得到bitmap后谭贪,在走一遍transformations俭识,對bitmap設(shè)置不同效果,得到最終的效果后套媚,回調(diào) onSuccess 中堤瘤,也就是ImageViewTarget 的onSuccess方法中
override fun onSuccess(result: Drawable) = setDrawable(result)
protected open fun setDrawable(drawable: Drawable?) {
(view.drawable as? Animatable)?.stop()
view.setImageDrawable(drawable)
updateAnimation()
}
最后設(shè)置對應的圖片。