Coil最简使用及流程分析

662 阅读3分钟

Coil Android端最简单使用及源码流程分析

1. 初步使用

  • imageView.load("htp://www.jpg")
  • imageView.load("/sdcard/a.jpg")
  • imageView.load(R.drawble.aa)

以上使用的是ImageView的扩展函数来进行调用的。

2.跟踪分析

  1. Image.load(xxx:Any, imageLoader:ImageLoader = context.imageloader, builder:ImageRequest.Builder.()->Unit = {})
  • 第一个参数xxx,是加载的内容,例如urlfilefilePath等等
  • 第二个参数是加载的ImageLoader,提供了默认的context.imageloader
  • 第三个参数是builder,是ImageRequest.Builder的扩展函数,供用户在外面进行扩展
  1. 分析第二个参数,ImageLoader

默认拿的是ImageViewContext的扩展函数的imageLoader,追踪进去,调用的是Coil.imageLoader(this)。 最终会调用到newImageLoader

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
    }
  • imageLoaderFactory?.newImageLoader(),这里看,如果手动实现了ImageLoaderFactory并代码设置了,则使用设置的ImageLoader
  • (context.applicationContext as? ImageLoaderFactory)如果在自定义的Application实现了ImageLoaderFactory,则使用这里的ImageLoader
  • 都没实现,则直接new一个ImageLoader对象使用
  1. 第三个参数:ImageRequest.Builder的扩展函数

这里在最终调用loadAny的方法离,new了必须的ImageRequest.Builder对象

val request = ImageRequest.Builder(context)
        .data(data)
        .target(this)
        .apply(builder)
        .build()

同时这里apply到用户对Builder扩展函数里添加的一些属性,这里看到用户对builder的配置是优先级比这个高的。 imageLoader.enqueue(request)这里就是终极调用了,并且返回的是Disposable 到这里,表面流程已经走完了,下面我们再看细分流程

3. ImageLoader解析
  1. 我们自己不实现ImageLoaderFactory接口的话,会自动实例化一个ImageLoader对象,调用它的Builder的build方法实际生成了RealImageLoader对象
  2. 这里我们专注 RealImageLoader对象的enqueue(request:ImageRequest)方法
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)
        }
    }
  • 首先这里构造了scope
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate +
        CoroutineExceptionHandler { _, throwable -> logger?.log(TAG, throwable) })

熟悉协程的话,很明显,这里它自己构造了一个CoroutineScope 经典的SupervisorJob,还有主线程的Dispatchers.Main,加入异常的Handler

  • 然后调用scopelaunch方法来启动协程,注意:协程体里就是启动加载图片的主流程啦 即:
executeMain(request, REQUEST_TYPE_ENQUEUE)

  1. executeMain逐条分析
    • 首先复制一份Request val request = initialRequest.newBuilder().defaults(defaults).build()
    • 创建EventListener
    • 创建TargetDelegate,实际获得 PoolableTargetDelegate
    • 创建RequestDelegate 实际获得ViewTargetRequestDelegate
    • 开始绑定生命周期request.lifecycle.awaitStarted()
    • 检查是否有缓存可用.... 这里暂时忽略缓存的处理解析
    • 计算解决Size问题 request.sizeResolver.size()
    • 核心方法:executeChain
      • 实例化RealInterceptorChain对象,并调用chain.proceed(request) 方法
      • 解析proceed方法:
        • 获取EngineInterceptor类(基于RealImageLoader的变量interceptors获取的),在变量赋值时期添加了EngineInterceptor实例
        • 这里调用intercept()方法
          • (1). registry.mapData(data) 这里获取load(data = "http://www.22.jpg")里的data对应map后得到的对象,例如Uri,File等对象
          • (2).然后通过registry.requireFetcher(mappedData) 根据load(data)的data来获取对应的Fetcher
          • (3).通过Fetcher及缓存Key算法来获取缓存的value,如果不为null的话,则返回此value
          • (4).如果为空,则通过withContext(request.dispatcher) { loadValue() }来进行协程请求数据,我们通过追踪可以看到dispaer不特别指定的话,应为DefaultRequestOptions里的默认Dispatchers.IO,即这里请求数据切换到了IO线程,不需要我们手动切换。
          • (5). 调用execute(mappedData,fetcher....)方法来启动获取图片流程。
          • (6).调用fetcher.fetch方法,调用对应的注册的各种Fetcherfetch方法。
          • (7).比如加载http://www.aaaa.com/a.jpg的网络图片,则流程会进入到HttpFetcherfetcher方法里。
          • (8).这里看到的是默认调用OkHttp的方法去获取Response,返回```SourceResult(source...)对象
          • (9).通过注册的registry来获取对应的Decoder,根据代码发现默认为BitmapFactoryDecoder decode(source)来获取DecoeResult,具体流程可以参照decodeInterruptible(...)方法。
          • (10).这里将DecodeResult包装为DrawableResult来返回

ImageView.load(url:String)至此最普通的流程分析完毕,下次我们再分析它的缓存以及扩展