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的文件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容

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