Coil Android端最简单使用及源码流程分析
1. 初步使用
imageView.load("htp://www.jpg")imageView.load("/sdcard/a.jpg")imageView.load(R.drawble.aa)
以上使用的是ImageView的扩展函数来进行调用的。
2.跟踪分析
Image.load(xxx:Any, imageLoader:ImageLoader = context.imageloader, builder:ImageRequest.Builder.()->Unit = {})
- 第一个参数xxx,是加载的内容,例如
url,file或filePath等等 - 第二个参数是加载的
ImageLoader,提供了默认的context.imageloader - 第三个参数是
builder,是ImageRequest.Builder的扩展函数,供用户在外面进行扩展
- 分析第二个参数,
ImageLoader
默认拿的是
ImageView的Context的扩展函数的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对象使用
- 第三个参数:
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解析
- 我们自己不实现
ImageLoaderFactory接口的话,会自动实例化一个ImageLoader对象,调用它的Builder的build方法实际生成了RealImageLoader对象 - 这里我们专注
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
- 然后调用
scope的launch方法来启动协程,注意:协程体里就是启动加载图片的主流程啦 即:
executeMain(request, REQUEST_TYPE_ENQUEUE)
executeMain逐条分析- 首先复制一份
Requestval 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方法,调用对应的注册的各种Fetcher的fetch方法。 - (7).比如加载
http://www.aaaa.com/a.jpg的网络图片,则流程会进入到HttpFetcher的fetcher方法里。 - (8).这里看到的是默认调用
OkHttp的方法去获取Response,返回```SourceResult(source...)对象 - (9).通过注册的
registry来获取对应的Decoder,根据代码发现默认为BitmapFactoryDecoder并decode(source)来获取DecoeResult,具体流程可以参照decodeInterruptible(...)方法。 - (10).这里将
DecodeResult包装为DrawableResult来返回
- (1).
- 获取EngineInterceptor类(基于
- 实例化
- 首先复制一份
ImageView.load(url:String)至此最普通的流程分析完毕,下次我们再分析它的缓存以及扩展