2020-12-14 coil 源碼流程分析

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è)置對應的圖片。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姨俩,一起剝皮案震驚了整個濱河市师郑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌张遭,老刑警劉巖地梨,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宝剖,死亡現(xiàn)場離奇詭異,居然都是意外死亡万细,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門聘裁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弓千,“玉大人,你說我怎么就攤上這事镣陕“葡裕” “怎么了总寒?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵摄闸,是天一觀的道長。 經(jīng)常有香客問我年枕,道長熏兄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任桥状,我火速辦了婚禮硝清,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘士飒。我一直安慰自己蔗崎,他們只是感情好,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布裙盾。 她就那樣靜靜地躺著,像睡著了一般庐完。 火紅的嫁衣襯著肌膚如雪徘熔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機與錄音懂讯,去河邊找鬼褐望。 笑死串前,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的谨读。 我是一名探鬼主播坛吁,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼拨脉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了填具?” 一聲冷哼從身側(cè)響起匆骗,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盟广,沒想到半個月后瓮钥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烹吵,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡肋拔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年凉蜂,在試婚紗的時候發(fā)現(xiàn)自己被綠了性誉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡纫雁,死狀恐怖倾哺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情闲勺,我是刑警寧澤扣猫,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布申尤,位于F島的核電站衙耕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏橙喘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一饰潜、第九天 我趴在偏房一處隱蔽的房頂上張望彭雾。 院中可真熱鬧锁保,春花似錦半沽、人聲如沸吴菠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜂挪。三九已至,卻和暖如春谬哀,著一層夾襖步出監(jiān)牢的瞬間严肪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工篇梭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留恬偷,地道東北人。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓袍患,卻偏偏與公主長得像诡延,于是被迫代替她去往敵國和親古胆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容