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

coil源码流程分析

先看简单使用

        iv_image.load(
            "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=80737749,1866961412&fm=26&gp=0.jpg"
        ) {
            
        }

直接使用imageview 的拓展函数加载图片,点进去:

@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 的方式构建一个request,用enqueue方法执行,其中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.
        //这个应该是协程,不过是运行在主线程中的
        val job = scope.launch {
            //执行
            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)
        }
    }

继续往下,看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.
        //创建一个新的request
        val request = initialRequest.newBuilder().defaults(defaults).build()

        // Create a new event listener.
        //创建回调监听
        val eventListener = eventListenerFactory.create(request)

        // Wrap the target to support bitmap pooling.
        //创建请求回调监听,用于得到请求结果时设置图片
        val targetDelegate = delegateService.createTargetDelegate(request.target, type, eventListener)

        // Wrap the request to manage its lifecycle.
        //创建lifecycle监听
        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.
           //设置占位图
            val cached = memoryCacheService[request.placeholderMemoryCacheKey]?.bitmap
            try {
                targetDelegate.metadata = null
                //  targetDelegate 就是 ImageViewTarget 对象, 调用onStart 方法,设置对应的占位图
                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.
            //执行,得到结果
            val result = executeChain(request, type, size, cached, eventListener)

            // Set the result on the target.
            //判断请求结果,设置成功失败
            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)
            //获取对应的数据
            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)
            //转换,将string 类型的data转换成Uri类型
            val mappedData = registry.mapData(data)
            eventListener.mapEnd(request, mappedData)

            // Check the memory cache.
            //获取可以处理这次请求的fetcher ,这里只是获取,没有实际获取
            val fetcher = request.fetcher(mappedData) ?: registry.requireFetcher(mappedData)
            //获取内存缓存的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.
            //根据获取的缓存,判断缓存策略,如果有缓存,并且设置了缓存策略,直接返回缓存
            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.
            //没有缓存,协程切换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.
                //执行请求
                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.
                //返回结果
                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)
            }
        }
    }

根据上面分析,走到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请求对应的数据
        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 {
                        //解码请求的数据
                        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.
        //转换得到的图片,如图片加圆角、渐变、高斯模糊
        val finalResult = applyTransformations(baseResult, request, size, options, eventListener)
        (finalResult.drawable as? BitmapDrawable)?.bitmap?.prepareToDraw()
        return finalResult
    }

调用fetcher.fetch方法获取数据,其中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>
}

上面的方法,就是循环fetchers,判断对应的class类型和handles方法获取对应的fetcher,其中fetchers的初始化在RealImageLoader中


    private val registry = componentRegistry.newBuilder()
        // Mappers 
        //添加转换器,上面讲string 转换成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()

通过循环获取,所以第一次获取网络的图片是,使用的是HttpUriFetcher
调用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设置网络和本地缓存策略
        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)
            }
        }
        //请求得到结果
        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
        )
    }

这里设置了网络、本地缓存,都是基于okhttp设置的。得到了HTTP请求的数据,通过decode进行解码,其中decode为BitmapFactoryDecoder,decode方法主要是根据Source计算bitmap大小,获取对应的bitmap,得到bitmap后,在走一遍transformations,对bitmap设置不同效果,得到最终的效果后,回调 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()
    }

最后设置对应的图片。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容