图片加载框架

xiaoxiao2021-02-28  68

图像加载

一般来说一图片加载框架应该具有以下基本特性:

1、图片的同步/异步加载

2、图片缓存(内存缓存/磁盘缓存)

3、网络加载

4、图片处理(压缩、裁剪、左右变幻等)

基本使用

UIL

1、设置全局配置 ImageLoaderConfiguration config = new ImageLoaderConfiguration .Builder(context) .memoryCacheExtraOptions(480, 800) // max width, max height,即保存的每个缓存文件的最大长宽 .threadPoolSize(3)//线程池内加载的数量 .threadPriority(Thread.NORM_PRIORITY - 2) .denyCacheImageMultipleSizesInMemory() .memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024)) // You can pass your own memory cache implementation/你可以通过自己的内存缓存实现 .memoryCacheSize(2 * 1024 * 1024) .discCacheSize(50 * 1024 * 1024) .discCacheFileNameGenerator(new Md5FileNameGenerator())//将保存的时候的URI名称用MD5 加密 .tasksProcessingOrder(QueueProcessingType.LIFO) .discCacheFileCount(100) //缓存的文件数量 .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) .imageDownloader(new BaseImageDownloader(context, 5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)超时时间 .writeDebugLogs() // Remove for release app .build();//开始构建 ImageLoader.getInstance().init(config); 2、设置具体ImageView配置 DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.ic_launcher) //设置图片在下载期间显示的图片 .showImageForEmptyUri(R.drawable.ic_launcher)//设置图片Uri为空或是错误的时候显示的图片 .showImageOnFail(R.drawable.ic_launcher) //设置图片加载/解码过程中错误时候显示的图片 .cacheInMemory(true)//设置下载的图片是否缓存在内存中 .cacheOnDisc(true)//设置下载的图片是否缓存在SD卡中 .considerExifParams(true) //是否考虑JPEG图像EXIF参数(旋转,翻转) .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)//设置图片以如何的编码方式显示 .bitmapConfig(Bitmap.Config.RGB_565)//设置图片的解码类型// .decodingOptions(android.graphics.BitmapFactory.Options decodingOptions)//设置图片的解码配置 //.delayBeforeLoading(int delayInMillis)//int delayInMillis为你设置的下载前的延迟时间 //设置图片加入缓存前,对bitmap进行设置 //.preProcessor(BitmapProcessor preProcessor) .resetViewBeforeLoading(true)//设置图片在下载前是否重置,复位 .displayer(new RoundedBitmapDisplayer(20))//是否设置为圆角,弧度为多少 .displayer(new FadeInBitmapDisplayer(100))//是否图片加载好后渐入的动画时间 .build();//构建完成 3、展示图片 ImageLoader.getInstance().displayImage(uri.getPath(), im, options, new ImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { } @Override public void onLoadingCancelled(String imageUri, View view) { } });

fresco

Fresco.initialize(getApplicationContext()); SimpleDraweeView testFre;//需要在xml布局文件中使用fresco自定义的布局和设置 testFre.setImageURI(uri);

picasso&glide

ImageView img; Glide.with(this) .load(uri) .into(img); Picasso.with(this) .load(uri) .into(img);

想比起UIL繁杂的设置和用法,其他的则显得简洁得多。

UIL

Universal Imager loader是比较悠久的图片加载框架。 其核心类是ImageLoader类,采用经典的单例模式,核心的方法有初始化设置的:

public synchronized void init(ImageLoaderConfiguration configuration)

以及展示图片的一系列重载方法:

displayImage()和loadImage();

以及操作缓存及一些基本配置的方法。 框架结构设计相对清晰明了。

其中ImageLoaderConfiguration采用的是Builder设计模式,主要功能是为ImageLoader配置一些设置,如缓存大小、线程数量、默认DisplayImageOptions设置等。DisplayImageOptions也是采用的Builder设计模式,但是主要是正对特定的ImageView进行设置,主要配置是否缓存,圆角、动画、默认显示图片、网络获取失败显示图片等。 1、在display()方法中首先会判断url是否为空,如果为空先会判断在DisplayImageOptions对象options中是否设置了Uri为空或是错误的时候显示的图片,如果有则加载给图片,否则直接设置图片为空,并返回renturn结束方法。

2、一旦uri不为空,那么首先会从内存缓存中取得相应的Bitmap(注意不是磁盘缓存),如果Bitmap不为空并且没有被回收,如果不需要进行处理,那么直接加载图片,否则会构建一个ProcessAndDisplayImageTask对象,其本质上是一个Runnable即线程,如果在options中设置了同步加载那么会直接执行线程的run方法,否则使用异步加载,即通过缓存线程池(taskExecutorForCachedImages)执行线程。

3、如果2中的Bitmap为空,即缓存为空或者已经被回收了,如果设置了加载过程中显示的图片那么设置为该图片,然后构造一个LoadAndDisplayImageTask,其本质上也是一个线程,也分同步和异步执行。

fresco

fresco是由facebook开源的图片加载框架,导入的时候fresco会自动的导入5个包:fresco、fbcore、drawee、imagepipeline、imagepipeline-base。其构成是有些复杂的。采用典型的MVC设计模式。有点晕。。。下午写完

首先需要在application中调用Fresco.initialize(Context context,@Nullable ImagePipelineConfig imagePipelineConfig,@Nullable DraweeConfig draweeConfig)),这一步看似无关紧要,其实是串联整个流程的至关重要一部,在这里,初始化了context,imagePipelineConfig(如果为null,则通过传递的context进而新建一个ImagePipelineConfig)创建了一个ImagePipelineFactory对象,并且调用initializeDrawee(context, draweeConfig),在其中初始化了一个PipelineDraweeControllerBuilderSupplier,同时通过 SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier),将sDraweeControllerBuilderSupplier传递到具体的View中。

其次Fresco的使用很简单,在全局初始化完成之后,直接调用setImageURI(Uri uri)方法即可实现所有的流程。

public void setImageURI(Uri uri, @Nullable Object callerContext) { DraweeController controller = mSimpleDraweeControllerBuilder .setCallerContext(callerContext) .setUri(uri) .setOldController(getController()) .build(); setController(controller); }

注意这里的controller是一个SimpleDraweeControllerBuilder对象向上转型。关键实现是setController(controller),一步步追踪进去:(DraweeView.java)setController()–>(DraweeHolder.java)setController()–>(DraweeHolder.java)attachController()–>mController.onAttach(),此时来到AbstractDraweeController以及PipelineDraweeController中的onAttach()方法,核心为submitRequest();此时已经接触到了框架的核心实现。mDataSource = getDataSource();定位方法,发现AbstractDraweeController类中只是定义了一个抽象方法,其具体实现在其子类PipelineDraweeController中:

@Override protected DataSource<CloseableReference<CloseableImage>> getDataSource() { if (FLog.isLoggable(FLog.VERBOSE)) { FLog.v(TAG, "controller %x: getDataSource", System.identityHashCode(this)); } return mDataSourceSupplier.get(); }

mDataSourceSupplier是全局初始化的时候传递的PipelineDraweeControllerBuilderSupplier,定位到其中的get()方法,发现是新建了一个PipelineDraweeControllerBuilder实例,最终会执行其中的obtainController()方法,然后进入到AbstractDraweeControllerBuilder中的obtainDataSourceSupplier()方法,并调用getDataSourceSupplierForRequest方法以及getDataSourceForRequest()方法,具体的实现在PipelineDraweeControllerBuilder中:

@Override protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest( ImageRequest imageRequest, Object callerContext, CacheLevel cacheLevel) { return mImagePipeline.fetchDecodedImage( imageRequest, callerContext, convertCacheLevelToRequestLevel(cacheLevel)); }

定位到fetchDecodedImage中:

public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage( ImageRequest imageRequest, Object callerContext, ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) { try { //获取数据 Producer<CloseableReference<CloseableImage>> producerSequence = mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest); //主要是传递数据 return submitFetchRequest( producerSequence, imageRequest, lowestPermittedRequestLevelOnSubmit, callerContext); } catch (Exception exception) { return DataSources.immediateFailedDataSource(exception); } }

定位getDecodedImageProducerSequence(imageRequest),进入到ProducerSequenceFactory中的getDecodedImageProducerSequence,核心实现为:

public Producer<Void> getDecodedImagePrefetchProducerSequence( ImageRequest imageRequest) { return getDecodedImagePrefetchSequence(getBasicDecodedImageSequence(imageRequest)); } private Producer<CloseableReference<CloseableImage>> getBasicDecodedImageSequence( ImageRequest imageRequest) { Preconditions.checkNotNull(imageRequest); Uri uri = imageRequest.getSourceUri(); Preconditions.checkNotNull(uri, "Uri is null."); switch (imageRequest.getSourceUriType()) { case SOURCE_TYPE_NETWORK: return getNetworkFetchSequence(); case SOURCE_TYPE_LOCAL_VIDEO_FILE: return getLocalVideoFileFetchSequence(); case SOURCE_TYPE_LOCAL_IMAGE_FILE: return getLocalImageFileFetchSequence(); case SOURCE_TYPE_LOCAL_CONTENT: return getLocalContentUriFetchSequence(); case SOURCE_TYPE_LOCAL_ASSET: return getLocalAssetFetchSequence(); case SOURCE_TYPE_LOCAL_RESOURCE: return getLocalResourceFetchSequence(); case SOURCE_TYPE_QUALIFIED_RESOURCE: return getQualifiedResourceFetchSequence(); case SOURCE_TYPE_DATA: return getDataFetchSequence(); default: throw new IllegalArgumentException( "Unsupported uri scheme! Uri is: " + getShortenedUriString(uri)); } }

到这里源码就比较清晰了,会根据不同情况,使用不同的获取序列数据。 ps:Fresco是笔者看到过的源代码最复杂的图片框架,复杂到分了5个包和铺天盖地的类,虽然其使用较简单,但是使用起来的是时候总感觉太重。

picasso

该框架采用的单例模式,通过Picasso.with(this)方法取得Picasso对象的实例。

public static Picasso with(Context context) { if (singleton == null) { synchronized (Picasso.class) { if (singleton == null) { singleton = new Builder(context).build(); } } } return singleton; }

可以看出这里还采用了Builder模式,Builder中初始化了关键的一些参数,如:线程池、下载器、缓存等。

然后调用load(Uri uri)方法,返回一个RequestCreator对象,构造器传递了一个Picasso对象的实例,并且将uri封装初始化了一个Request.Builder()对象。

再调用with(imageview,callback)方法,如果有内存缓存则直接设置显示图片,没有则将imageview以及request打包成一个Action对象,同时调用picasso.enqueueAndSubmit(action),此时运行流程又回到了Picasso对象中,然后调用dispatcher.dispatchSubmit(action),进入Dispatcher,这是一个分发器,使用了Handler消息机制,最终的运行落实在performSubmit、performCancel等一些列perform方法上,performSubmit方法主要是进行网络获取,通过submit方法在线程池中运行BitmapHunter线程,在BitmapHunter的run()方法中核心是Bitmap result = hunt(),其中会调用requestHandler.load(data, networkPolicy),RequestHandler共有7个具体的实现类。

网络获取的类为:NetworkRequestHandler,在构造器中初始了一个下载器Downloader类,Downloader是一个接口,具体的实现类有两个:OkHttpDownloader和UrlConnectionDownloader。 其中OkHttpDownloader封装的是OkHttp,而UrlConnectionDownloader封装的是HttpURLConnection,默认使用OkHttpDownloader。NetworkRequestHandler中重载了load方法,通过Response response = downloader.load(request.uri, request.networkPolicy)取得最终的响应,取得InputStream,并最终封装成为一个new Result(is, loadedFrom)对象。

glide

glide与picasso的较为相似,相似度为90%。首先调用with()方法,但是glide做了一定程度的优化,picasso中with方法必须以context为参数,glide中则支持Activity、Fragment等,此方法会返回一个RequestManager。

然后调用RequestManager中的load方法,会返回一个RequestBuilder对象,调用该类的into方法,会触发buildRequest(target),返回一个SingleRequest实例,然后调用 requestManager.track(target, request)–>requestTracker.runRequest(request)–> request.begin(),也就是最终会调用SingleRequest的begin()方法,会调用onSizeReady以及其中的engine.load,然后调用其中的

engineJob.addCallback(cb); engineJob.start(decodeJob);

其中engineJob代表线程池,decodeJob代表具体的线程。所以最终会调用其中的run()方法。可以看到其核心实现是runWrapped()方法,而runWrapped()中只存在一个一个switch语句,如果不细看会造成很大的困扰,runReason的初始值在构造器中初始为INITIALIZE,并且这里通过getNextStage和getNextGenerator不断的,遍历stage的五个值(),同时根据stage选定不同的DataFetcherGenerator,并且在runGenerators()方法中,不断的调用DataFetcherGenerator的startNext()方法,并且回调FetcherReadyCallback方法,然后在其中进行相应的处理。startNext()方法中关键代码如下:

loadData.fetcher.loadData(helper.getPriority(), this);

实际调用的是DataFetcher的loadData方法。DataFetcher的具体实现类有7个,会根据构造策略选用不同的DataFetcher。

后续

这四个图片框架,从使用便捷角度来说,UIL最复杂,fresco、picasso、glide差不多。 从源码角度来说,UIL和picasso逻辑最清晰,glide较复杂,比一般的网络框架都要复杂,fresco的复杂程度简直。。。或许作为一个单纯的图片框架,显得太重了,如果对图片显示不是太专业级别,那么不推荐使用Fresco。查看其包可以发现,其大小在达到了3M。picasso在120KB左右,UIL:160KB,glide:475KB。picasso是不错的选择,当需要在Activity或者Fragment中使用到时,可以参考使用glide。

转载请注明原文地址: https://www.6miu.com/read-63919.html

最新回复(0)