Android Fresco 笔记

一、简介使用

1.1 介绍

Fresco对于图片的展示支持多种情况:backgroud image(背景图)、placeholder image(占位图)、actual image(加载的图片)、progress bar image(进度条)、retry image(重新加载的图片)、failure image(失败图片)与overlay image(叠加图)。

主页:Fresco官方文档

1.2优点

1、支持webp格式的图片Fresco是通过jni来实现支持WebP格式图片。

2、5.0以下系统:使用”ashmem”(匿名共享内存)区域存储Bitmap缓存,这样Bitmap对象的创建、释放将永远不会触发GC,关于”ashmem”存储区域,它是一个不在Java堆区的一片存储内存空间,它的管理由Linux内核驱动管理,不必深究,只要知道这块存储区域是别于堆内存之外的一块空间就行了,且这块空间是可以多进程共享的,GC的活动不会影响到它。5.0以上系统,由于内存管理的优化,所以对于5.0以上的系统Fresco将Bitmap缓存直接放到了堆内存中。
3、使用了三级缓存:Bitmap缓存+未解码图片缓存+硬盘缓存。
其中前两个就是内存缓存,Bitmap缓存根据系统版本不同放在了不同内存区域中,而未解码图片的缓存只在堆内存中

1.3使用

Application中初始化

 public class MyApplication extends Application {
   @Override
  public void onCreate() {
    super.onCreate();
    Fresco.initialize(this);
}
 }

xml

 <com.facebook.drawee.view.SimpleDraweeView
   android:id="@+id/my_image_view"
  android:layout_width="130dp"
  android:layout_height="130dp"
  fresco:placeholderImage="@drawable/my_drawable"
/>

具体

  Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/fresco-logo.png");
  SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
  draweeView.setImageURI(uri);

二、原理分析

DraweeView采用的 是典型的mvc模式,
DraweeHierarchy 负责的是跟显示相关的,
DraweeController 负责的是后台相关的,
DraweeHolder主要是统筹两者的,Fresco的初始化方法执行后会初始化相关配置。

以SimpleDraweeView为例,它的controller是PipelineDraweeController,Fresco在初始化时初始化一个PipelineDraweeControllerBuilderSupplier来提供这个controller,DraweeHolder主要通过controller启动后台服务获取数据,setController方法执行后后调用controller的onAttach()方法,这个方法会提交请求并获得数据,后将数据返回给ui线程,

Fresco数据是封装成Supplier<DataSource<IMAGE>> 的形式返回,而真正将数据传递回来的是通过ImagePipeline,这个ImagePipeline会在PipelineDraweeControllerBuilderSupplier的构造器初始化创建一个,它会在Fresco初始化方法中进行初始化,SimpleDraweeView初始化方法中会调用PipelineDraweeControllerBuilderSupplier的get的方法将ImagePipeline返回,ImagePipeline有一个RequestListsner的成员变量,它管理这个一个监听列表,请求监听通过这个RequestListsner返回,而正确去获取数据的时候是启动一个后台服务进行数据获取的,整个数据获取有分多个阶段,Fresco通过producer划分这些阶段,比如缓存获取阶段、编码阶段、网络获取阶段,它会一个一个阶段去获取数据,如果producer1没有获取到数据就交给producer2进行获取数据以此类推下去,producer之间通过consumer返回数据,最终将数据返回到ui线程去。

2.1SimpleDraweeView的继承关系

Object (java.lang)
 View (android.view)
    ImageView (android.widget)
        DraweeView (com.facebook.drawee.view)  根部DraweeView
            GenericDraweeView (com.facebook.drawee.view)  通用的DraweeView
                SimpleDraweeView (com.facebook.drawee.view) 最简单的DraweeView

2.2从 SimpleDraweeView.setImageURI(uri);

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

         setController(controller);
 }

可以从字面看出来 给当前view 设置一个controller,一个DraweeView对应一个DraweeController。而Controller是由build模式所创建,查看 mControllerBuilder 的赋值来源

  private void init(Context context, @Nullable AttributeSet attrs) {
  ...
  mControllerBuilder = sDraweecontrollerbuildersupplier.get();
  ...
  }

这是在SimpleDraweeView的构造方法中会调用init()方法,继续查看sDraweecontrollerbuildersupplier是什么,

 public static void initialize(Supplier<? extends AbstractDraweeControllerBuilder> draweeControllerBuilderSupplier) {
     sDraweecontrollerbuildersupplier = draweeControllerBuilderSupplier;
 }

initialize 是的调用位置, Fresco中的initializeDrawee()

private static void initializeDrawee(Context context, @Nullable DraweeConfig draweeConfig) {
   ...
   sDraweeControllerBuilderSupplier =new PipelineDraweeControllerBuilderSupplier(context, draweeConfig);
   SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
   ...
 }

 public static void initialize(Context context, @Nullable ImagePipelineConfig imagePipelineConfig,  @Nullable DraweeConfig draweeConfig) {
   ...
   initializeDrawee(context, draweeConfig);
   ...
 }

这是static方法,Fresco在Application中执行Fresco.initialize(context)里 initializeDrawee(context, draweeConfig);
初始化Drawee相关配置信息。所以可以先搞清楚

sDraweeControllerBuilderSupplier 是 PipelineDraweeControllerBuilderSupplier
mControllerBuilder 是 PipelineDraweeControllerBuilder
controller 是 PipelineDraweeController

2.3setController(controller)

public void setController(@Nullable DraweeController draweeController) {
    mDraweeHolder.setController(draweeController);
    super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}

这里将控制器传进了holder里面。
mDraweeHolder.setController(draweeController)里最终调用了controller的onAttach()方法,

     @Override
  public void onAttach() {
       ......
      mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
      Preconditions.checkNotNull(mSettableDraweeHierarchy);
      mDeferredReleaser.cancelDeferredRelease(this);
      mIsAttached = true;
      if (!mIsRequestSubmitted) {
      submitRequest();
      }
    }

最终执行到 submitRequest();

2.4submitRequest();

  protected void submitRequest() {
      mEventTracker.recordEvent(Event.ON_DATASOURCE_SUBMIT);
      getControllerListener().onSubmit(mId, mCallerContext);
      mSettableDraweeHierarchy.setProgress(0, true);
      mIsRequestSubmitted = true;
      mHasFetchFailed = false;
     mDataSource = getDataSource();//获取数据
    .......
      final String id = mId;
      final boolean wasImmediate = mDataSource.hasResult();
      final DataSubscriber<T> dataSubscriber =
    new BaseDataSubscriber<T>() {
      @Override
      public void onNewResultImpl(DataSource<T> dataSource) {
        // isFinished must be obtained before image, otherwise we might set intermediate result
        // as final image.
        boolean isFinished = dataSource.isFinished();
        T image = dataSource.getResult();
        if (image != null) {
          onNewResultInternal(id, dataSource, image, isFinished, wasImmediate);
        } else if (isFinished) {
          onFailureInternal(id, dataSource, new NullPointerException(), /* isFinished */ true);
        }
      }
      @Override
      public void onFailureImpl(DataSource<T> dataSource) {
        onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
      }
    };
    
    mDataSource.subscribe(dataSubscriber,mUiThreadImmediateExecutor);//这个将获取的数据回调到ui线程 
}

这里可以看出来是 获取到数据,然后将数据提交到ui线程去。

2.5 如何获取数据getDataSource()

 @Override
   protected DataSource<CloseableReference<CloseableImage>> getDataSource() {
      .....
     return mDataSourceSupplier.get();
   }

这里出现了一个mDataSourceSupplier,mDataSourceSupplier只有在两个地方传递进去,一个是构造器,还有一个是initialize方法,而这个方法只有在PipelineDraweeControllerBuilder的obtainController()时调用到:

    private void init(Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier) {
        mDataSourceSupplier = dataSourceSupplier;
     }

 public void initialize( Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier, String id,
  CacheKey cacheKey, Object callerContext, @Nullable ImmutableList<DrawableFactory> customDrawableFactories,
  @Nullable ImageOriginListener imageOriginListener) {
  ...
   init(dataSourceSupplier);
  ...
 }

initialize() 方法是哪里调用的呢?所有的方法都是在PipelineDraweeControllerBuilder的 调用父类方法Builder 里面处理的

    protected PipelineDraweeController obtainController() {
            try {
                    ...
        controller.initialize(obtainDataSourceSupplier(controller, controllerId),
      controllerId,
      getCacheKey(),
      getCallerContext(),
      mCustomDrawableFactories,
      mImageOriginListener);
      controller.initializePerformanceMonitoring(mImagePerfDataListener);
     return controller;
     } finally {
      ...
   }
 }

父类 AbstractDraweeControllerBuilder.java

  protected AbstractDraweeController buildController() {
        ...
   AbstractDraweeController controller = obtainController();
   controller.setRetainImageOnFailure(getRetainImageOnFailure());
    controller.setContentDescription(getContentDescription());
    controller.setControllerViewportVisibilityListener(getControllerViewportVisibilityListener());
    ...
    return controller;
 }

  @Override
  public AbstractDraweeController build() {
    ...
    return buildController();
  }

mDataSourceSupplier 创建赋值的流程大概出来了

SimpleDraweeView.setImageURI() --> PipelineDraweeControllerBuilder.build() --> PipelineDraweeControllerBuilder.buildController() --> PipelineDraweeControllerBuilder.obtainController() --> PipelineDraweeController.initialize() --> 赋值完成

按照上述流程继续走下

mDataSourceSupplier 取值方法 obtainDataSourceSupplier(controller, controllerId)

protected Supplier<DataSource<IMAGE>> obtainDataSourceSupplier(
  final DraweeController controller, final String controllerId) {
...
// final image supplier;
if (mImageRequest != null) {
  supplier = getDataSourceSupplierForRequest(controller, controllerId, mImageRequest);
}

// increasing-quality supplier; highest-quality supplier goes first
if (supplier != null && mLowResImageRequest != null) {
  List<Supplier<DataSource<IMAGE>>> suppliers = new ArrayList<>(2);
  suppliers.add(supplier);
  suppliers.add(getDataSourceSupplierForRequest(controller, controllerId, mLowResImageRequest));
  supplier = IncreasingQualityDataSourceSupplier.create(suppliers, false);
}
...
return supplier;
}

发现最终是走到getDataSourceSupplierForRequest方法。

   protected Supplier<DataSource<IMAGE>> getDataSourceSupplierForRequest(
       final DraweeController controller, String controllerId, REQUEST imageRequest) {
      return getDataSourceSupplierForRequest(
    controller, controllerId, imageRequest, CacheLevel.FULL_FETCH);
 }

 protected Supplier<DataSource<IMAGE>> getDataSourceSupplierForRequest(
  final DraweeController controller,
  final String controllerId,
  final REQUEST imageRequest,
    final CacheLevel cacheLevel) {
   final Object callerContext = getCallerContext();
     return new Supplier<DataSource<IMAGE>>() {
  @Override
  public DataSource<IMAGE> get() {
    // 这里就是 getDataSource() 时候真正调用的方法  
    return getDataSourceForRequest(
        controller, controllerId, imageRequest, callerContext, cacheLevel);
  }

  @Override
  public String toString() {
    ...
  }
};
 }

现在流程最终到getDataSourceForRequest()

2.5getDataSourceForRequest

现在流程为
onAttach() --> submitRequest() --> getDataSource() -->mDataSourceSupplier.get() --> getDataSourceForRequest()

   // PipelineDraweeControllerBuilder.java

     protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
  DraweeController controller,
  String controllerId,
  ImageRequest imageRequest,
  Object callerContext,
  AbstractDraweeControllerBuilder.CacheLevel cacheLevel) {

    return mImagePipeline.fetchDecodedImage(
    imageRequest,
    callerContext,
    convertCacheLevelToRequestLevel(cacheLevel),
    getRequestListener(controller));

}
可以看出来 最终把任务提交到mImagePipeline.fetchDecodedImage()去执行。

2.6ImagePipeline是什么

ImagePipeline负责完成加载图像,变成Android设备可呈现的形式所要做的每个事情。
pipelineSequence
大致流程如下:
1.检查内存缓存,如有,返回
2.后台线程开始后续工作
3.检查是否在未解码内存缓存中。如有,解码,变换,返回,然后缓存到内存缓存中。
4.检查是否在磁盘缓存中,如果有,变换,返回。缓存到未解码缓存和内存缓存中。
5.从网络或者本地加载。加载完成后,解码,变换,返回。存到各个缓存中。

至此流程基本结束

三、其他

3.1

Asynctask配合使用时候,注意阻塞问题,Android4.0以后Asynctask就改成(先进先出)谁先来谁先执行并且只能一个线程执行,这样就导致了所有Http请求都阻塞了。

3.2从缓存中读取文件

使用Fresco加载图片,预览保存图片不在下载而是直接从缓存中读取,

FileBinaryResource resource = (FileBinaryResource) Fresco.getImagePipelineFactory().getMainFileCache().getResource(new SimpleCacheKey(URI));
File file2 = resource.getFile();

说明:
1.Fresco不同版本略不同,老版本是getMainFileCache()。
2.得到文件路径是安装包路径,并且是.cnt未结尾。

/data/user/0/com.app.example/cache/image_cache/v2.ols100.1/67/MuA4Ubc_k3r3KFyVmpyoOw6RHU8.cnt

3.所以如果涉及到微信分享出去等,需要重新复制一个.jpg或png的文件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1. 本文适用网络仅为“高校校园网”,目前运营商未商用,暂不适用;2. Win 10 ipv6存在问题的根本原因是...
    Micro井阅读 152,067评论 9 20
  • 个人学习批处理的初衷来源于实际工作;在某个迭代版本有个BS(安卓手游模拟器)大需求,从而在测试过程中就重复涉及到...
    Luckykailiu阅读 10,318评论 0 11
  • NAME dnsmasq - A lightweight DHCP and caching DNS server....
    ximitc阅读 7,974评论 0 0
  • 首先用这种方法不是所有人都可以你可能必须要有<公网IP>才可以【电信宽带用户打电话180区号0000免费变更】 判...
    仁二阅读 36,938评论 0 5
  • 今天几件事 总算解决了三年级两个班级缺数学老师的难题,同时二年级一个班缺数学老师的问题也得了很好的处理,不过二年级...
    甲午之印阅读 1,427评论 0 0

友情链接更多精彩内容