当前位置: 首页 > 科技观察

Android官方Kotlin-First图片加载库详解

时间:2023-03-21 17:54:45 科技观察

前言Coil是一个非常年轻的图片加载库。它在2020年10月22日才发布了1.0.0版本,但由Android正式推广。AndroidDevelopersBackstage本博客有专聊。提升的原因比较简单:一方面,这个库做的很好,另一方面,这个库完全用Kotlin写的,使用了很多Kotlin的特性,尤其是coroutines。所以谷歌说不会放弃Java,其实大家都心知肚明。Coil名称的由来:来源于CoroutineImageLoader的首字母。可以看出图片加载是通过Kotlin协程进行的。特点如下:更快:Coil在性能上有很多优化,包括内存缓存和磁盘缓存,缩略图保存在内存中,通过BitmapPool回收Bitmap,自动挂起和取消网络请求等轻量级:Coil只有2000个方法,这与毕加索的数量差不多。与Glide和Fresco相比,它更加轻量易用:Coil的API充分利用了Kotlin的新特性,同时还具有丰富的扩展功能,简化并减少了大量的样板代码。流行的开源库从Coil的特性可以看出这是一个非常适合个人应用的图片加载库,尤其是纯Kotlin开发的应用。而且Coil使用了很多Kotlin的新特性和协程,这对我们学习Kotlin有很大的价值。与glide和fresco等结构非常复杂、代码量惊人的相比,Coil只有2000个左右的方法,因此也非常适合源码研究和学习。Coil的基本使用可以从mavenCentral()下载:implementation("io.coil-kt:coil:1.1.1")Coil为ImageView增加了很多扩展功能,所以我们只需一行代码就可以加载图片://URLimageView.load("https://www.example.com/image.jpg")//ResourceimageView.load(R.drawable.image)//FileimageView.load(File("/path/to/image.jpg"))同时,我们还可以使用lambda语法轻松配置图片加载:imageView。load("https://www.example.com/image.jpg"){crossfade(true)placeholder(R.drawable.image)transformations(CircleCropTransformation())}常用的APIImageLoaderImageLoader是图片加载的大管家Coil,负责处理缓存、数据获取、图像解码、请求管理、Bitmap缓存池、内存管理等。一般建议只创建一个ImageLoader并共享在App中,这样性能最优。这是因为每个ImageLoader都有自己的内存缓存和Bitmap缓存池。我们可以通过构造函数来创建和配置ImageLoader。valimageLoader=ImageLoader.Builder(context).availableMemoryPercentage(0.25).crossfade(true).build()同时,由于ImageLoader是一个接口,意味着我们可以很方便的对其进行测试。例如,我们可以注入一个伪造的ImageLoader,以便每次都返回相同的可绘制对象。valfakeImageLoader=object:ImageLoader{privatevaldrawable=ColorDrawable(Color.BLACK)overridefunenqueue(request:ImageRequest):Disposable{request.target?.onStart(drawable)request.target?.onSuccess(drawable)returndisposable}覆盖暂停funexecute(request:ImageRequest))ImageResult{returnSuccessResult(drawable=drawable,request=request,metadata=ImageResult.Metadata(memoryCacheKey=MemoryCache.Key(""),isSampled=false,dataSource=DataSource.MEMORY_CACHE,isPlaceholderMemoryCacheKeyPresent=false))}}ImageRequestImageLoad加载图像为提供所有必要的信息,我们也可以使用自定义的Target进行处理。valrequest=ImageRequest.Builder(context).data("https://www.example.com/image.jpg").target{drawable->//Handletheresult.}.build()context.imageLoader.enqueue(请求)ImageRequest是基于Builder模式创建的,包含加载图片的各种配置项。这里我们重点介绍最常用的配置项。DisposableDisposable是调用load()方法后的返回值,主要用于取消图片加载:interfaceDisposable{/***如果图片加载请求已经完成或取消,则返回true*/valisDisposed:Boolean/***取消正在进行的图片加载请求和相关资源的释放,并且该方法是幂等的*/fundispose()/***非阻塞等待任务结束*/@ExperimentalCoilApisuspendfunawait()}图片转换Imagetransformation在图片加载库中非常重要,Coil将其抽象为一个Transformation接口,用于实现常用的功能。可以看到transform()方法中有一个BitmapPool参数。这是因为在实现图形转换时经常需要Bitmap。这时候可以直接在BitmapPool中获取,这样就可以Reuse已有的Bitmap。interfaceTransformation{funkey():Stringsuspendfuntransform(pool:BitmapPool,input:Bitmap,size:Size):Bitmap}imageView.load("https://www.example.com/image.jpg"){transformations(CircleCropTransformation())}Coil主要提供这些图像变换的效果:功能扩展Coil不仅提供了很多必要的功能,还预留了很多扩展点供开发者实现定制化。Coil的图片加载主要包括四个主要模块:InterceptorsCoil的Interceptor无疑是借鉴了okhttp的设计思想,大大方便了后续的功能扩展。例如,我们可以为Coil添加一个自定义缓存层:(chain.request.data.toString())if(value!=null){returnSuccessResult(drawable=value.bitmap.toDrawable(context),request=chain.request,metadata=TODO())}returnchain.proceed(chain.request)}}Mappers和Fetchers调用load()时,传入的String参数可能指向本地资源文件或网络图片。Mappers和Fetchers可以一起使用来区分资源类型。例如:imageView.load("android.resource://example.package.name/drawable/image")imageView.load("https://www.example.com/image.jpg")StringMapper将传入的转换串入对应的Uri。internalclassStringMapper:Mapper{overridefunmap(data:String)=data.toUri()}ResourceUriFetcher会判断Uri的scheme类型是否为android.resource,是则代表本地资源文件,HttpUriFetcher会判断Uri的scheme类型是http还是https,如果是就表示网络图片。internalclassHttpUriFetcher(callFactory:Call.Factory):HttpFetcher(callFactory){overridefunhandles(data:Uri)=data.scheme="http"||data.scheme="https"overridefunkey(data:Uri)=data.toString()overridefunUri.toHttpUrl():HttpUrl=HttpUrl.get(toString())}DecodersAndroid支持很多图片格式,但也有很多格式它不支持(例如:Gif、SVG、视频帧等),所以Coil提供了相应的扩展库。①Gif(GifDecoder支持所有API级别,但速度较慢,ImageDecoderDecoder加载速度较快,但仅适用于API28及更高版本)implementation("io.coil-kt:coil-gif:1.1.1")valimageLoader=ImageLoader.Builder(context).componentRegistry{if(SDK_INT>=28){add(ImageDecoderDecoder())}else{add(GifDecoder())}}.build()②SVG(如果请求的MIME类型是image/svg+xml,所有SVG将被自动检测和解码)implementation("io.coil-kt:coil-svg:1.1.1")valimageLoader=ImageLoader.Builder(context).componentRegistry{add(SvgDecoder(context))}.build()③视频帧(只支持File和Uri)implementation("io.coil-kt:coil-video:1.1.1")valimageLoader=ImageLoader.Builder(context).componentRegistry{add(VideoFrameFileFetcher())add(VideoFrameUriFetcher())}。建造()