图片加载框架

图像加载

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

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值