Fresco源码解析 - DataSource怎样存储数据

datasource是一个独立的 package,与FB导入的guava包都在同一个工程内 - fbcore

datasource的类关系比较简单,一张类图基本就可以描述清楚它们间的关系。



DataSource 是一个 interface, 功能与JDK中的Future类似,但是相比于Future,它的先进之处则在于 不仅仅只生产一个单一的结果,而是能够提供系列结果

Unlike Futures, DataSource can issue a series of results, rather than just one.

最典型的用途就是渐进式加载图片时可以提供加载中的中间数据。



DataSubscriberDataSource 构成了一个观察者模式

Datasource 提供了注册方法。

void subscribe(DataSubscriber<T> dataSubscriber, Executor executor);

通过 subscribe 方法我们可以把 DataSubscriber 注册成为 DataSource 的观察者,然后当 DataSource 的数据发生变化时,在 Executor 中通知所有的观察者 - DataSubscriber

DataSubscriber 会响应数据的四种变化。

  1. onNewResult
  2. onFailure
  3. onCancellation
  4. onProgressUpdate

使用Executor来通知观察者是比较高明的,这样做可以让回调方法的执行线程交由 DataSubscriber 来处理,增加了灵活性。



DataSource 只是一个接口,没有提供任何实现,AbstractDataSource 实现了 DataSource 后封装了一些基础的操作,例如 通知观察者记录数据状态

Datasource 的状态记录使用了一个枚举类型。

private enum DataSourceStatus {
  // data source has not finished yet
  IN_PROGRESS,

  // data source has finished with success
  SUCCESS,

  // data source has finished with failure
  FAILURE,
}

这三种状态保存在一个成员变量(mDataSourceStatus)中。

@GuardedBy("this")
private DataSourceStatus mDataSourceStatus;

AbstractDataSource 构造时,会把 mDataSourceStatus 设置为 IN_PROGRESS

protected AbstractDataSource() {
  mIsClosed = false;
  mDataSourceStatus = DataSourceStatus.IN_PROGRESS;
  mSubscribers = new ConcurrentLinkedQueue<>();
}

所有的观察者(订阅者)会被放在一个列表中 - mSubscribers

private final ConcurrentLinkedQueue<Pair<DataSubscriber<T>, Executor>> mSubscribers;

如果当前的数据请求没有关闭并且满足mDataSourceStatus == DataSourceStatus.IN_PROGRESS时才能注册成功观察者,因为只有当数据发生变化的时候,观察者才有存在的意义。

  @Override
public void subscribe(final DataSubscriber<T> dataSubscriber, final Executor executor) {
  Preconditions.checkNotNull(dataSubscriber);
  Preconditions.checkNotNull(executor);
  boolean shouldNotify;

  synchronized(this) {
    if (mIsClosed) {
      return;
    }

    if (mDataSourceStatus == DataSourceStatus.IN_PROGRESS) {
      mSubscribers.add(Pair.create(dataSubscriber, executor));
    }

    shouldNotify = hasResult() || isFinished() || wasCancelled();
  }

  if (shouldNotify) {
    notifyDataSubscriber(dataSubscriber, executor, hasFailed(), wasCancelled());
  }
}

如果 DataSource 有了新的数据或者请求已经结束掉或被取消掉,会通知观察者。

private void notifyDataSubscribers() {
  final boolean isFailure = hasFailed();
  final boolean isCancellation = wasCancelled();
  for (Pair<DataSubscriber<T>, Executor> pair : mSubscribers) {
    notifyDataSubscriber(pair.first, pair.second, isFailure, isCancellation);
  }
}

private void notifyDataSubscriber(
    final DataSubscriber<T> dataSubscriber,
    final Executor executor,
    final boolean isFailure,
    final boolean isCancellation) {
  executor.execute(
      new Runnable() {
        @Override
        public void run() {
          if (isFailure) {
            dataSubscriber.onFailure(AbstractDataSource.this);
          } else if (isCancellation) {
            dataSubscriber.onCancellation(AbstractDataSource.this);
          } else {
            dataSubscriber.onNewResult(AbstractDataSource.this);
          }
        }
      });
}


使用 DataSource 很重要的一点:不要产生内存泄露,也就是说,用过的资源一定要释放掉。

使用 DataSubscriber 注册成为了观察者后,回调方法都会带回一个 DataSource 的实例,如果请求已经结束后者失败了,拿到数据后一定要把 DataSourceclose 掉,否则很容易造成 OOM。

BaseDataSubscriber 就是为了防止OOM,它本身的设计也很巧妙。

在毁掉方法 onNewResultonFailure 中加了一个 try - catch, 在 try 的 block 中调用子类必须重载的 onNewResultImpl 方法,然后在 finally 的 block 中 close DataSource

DataSourceSubscriber.java

public abstract class BaseDataSubscriber<T> implements DataSubscriber<T> {

  @Override
  public void onNewResult(DataSource<T> dataSource) {
    try {
      onNewResultImpl(dataSource);
    } finally {
      if (dataSource.isFinished()) {
        dataSource.close();
      }
    }
  }

  @Override
  public void onFailure(DataSource<T> dataSource) {
    try {
      onFailureImpl(dataSource);
    } finally {
      dataSource.close();
    }
  }

  @Override
  public void onCancellation(DataSource<T> dataSource) {
  }

  @Override
  public void onProgressUpdate(DataSource<T> dataSource) {
  }

  protected abstract void onNewResultImpl(DataSource<T> dataSource);

  protected abstract void onFailureImpl(DataSource<T> dataSource);
}


IncreasingQualityDataSourceSupplierFirstAvailableDataSourceSupplierDataSource 的两种不同的数据存储形式,等后面用到了再做分析。



Supplier 是一个设计比较巧妙的借口,用途非常广泛。

A class that can supply objects of a single type. Semantically, this could be a factory, generator, builder, closure, or something else entirely. No guarantees are implied by this interface.



SettableDataSourceset 方法中使用 GuavaPreconditions 来做数据合法性检验,它与 DataSource 的区别也是仅此而已。

Preconditions

  • checkArgument
  • checkState
  • checkNotNull
  • checkElementIndex
  • checkPositionIndex
  • checkPositionIndexes

如果 check 结果为 false, 则抛出异常。Fresco 的错误处理基本上是用异常做的。

时间: 2024-10-11 06:06:14

Fresco源码解析 - DataSource怎样存储数据的相关文章

Fresco源码解析 - 初始化过程分析

使用Fresco之前,一定先要进行初始化,一般初始化的工作会在Application.onCreate()完成,当然也可以在使用Drawee之前完成. Fresco本身提供了两种初始化方式,一种是使用使用默认配置初始化,另一种是使用用户自定义配置. 如下代码是Fresco提供的两个初始化方法.第一个只需要提供一个Context参数,第二个还需要提供 ImagePipeline 的配置实例 - ImagePipelineConfig. /** Initializes Fresco with the

Fresco源码解析 - 创建一个ImagePipeline(一)

在Fresco源码解析 - 初始化过程分析章节中,我们分析了Fresco的初始化过程,两个initialize方法中都用到了 ImagePipelineFactory类. ImagePipelineFactory.initialize(context);会创建一个所有参数都使用默认值的ImagePipelineConfig来初始化ImagePipeline. ImagePipelineFactory.initialize(imagePipelineConfig)会首先用 imagePipelin

Fresco源码解析 - 本地编译

第一次写专栏,如有表述不好或者理解错误的地方,请各位读者不吝赐教,本人一定虚心接受并第一时间改正. 作为专题第一篇,先从最简单的开始,顺便找找感觉. Fresco 是 facebook 在今年的 F8 大会上宣布开源的一个用于加载图片的库,它不仅支持多种图片文件格式,而且由于使用了pinned purgeables 技术,使得大图加载过程中产生OOM的概率大大降低,对开发者来说绝对是一件喜大普奔的事情,对于像天猫HD这样需要加载大量图片的APP来说也绝对是个福音. 下载代码 首先把源码从 Git

Fresco源码解析 - DraweeView

DraweeView 是Fresco的三大组件(Hierarchy.Controller.View) 之一,作为MVC模式中的 View,主要负责显示由 Hierarchy 提供的数据(placeholder.actual image.progress drawable等),Controller 作为幕后,负责获取数据. 继承体系 DraweeView 并不是是一个简单的自定义View,它必须要提供与 Hierarchy 和 Controller 交互的接口,DraweeView 的继承关系如下

Fresco源码解析 - Hierarachy-View-Controller

Fresco是一个MVC模型,由三大组件构成,它们的对应关系如下所示: M -> DraweeHierarchy V -> DraweeView C -> DraweeController M 所对应的 DraweeHierarchy 是一个有层次结构的数据结构,DraweeView 用来显示位于 DraweeHierarchy 最顶层的图像(top level drawable),DraweeController 则用来控制 DraweeHierarchy 的顶层图像是哪一个. o F

mapbox.gl源码解析——基本架构与数据渲染流程

加载地图 Mapbox GL JS是一个JavaScript库,使用WebGL渲染交互式矢量瓦片地图和栅格瓦片地图.WebGL渲染意味着高性能,MapboxGL能够渲染大量的地图要素,拥有流畅的交互以及动画效果.可以显示立体地图并且支持移动端,是一款十分优秀的WEB GIS开发框架. 在页面引入MapboxGL脚本库和样式库: <script src='https://api.mapbox.com/mapbox-gl-js/v0.40.0/mapbox-gl.js'></script&g

Fresco 源码解析 - 利用 @DoNotSkip 来防止混淆

我们都知道,如果打开了混淆开关,代码 release 阶段会根据 proguard 规则进行混淆,但是有些实体类(例如 json 字符串对应的 model)需要进行序列化.反序列化,而序列化工具(例如 Gson.fastjson)是利用反射来一一对应 json 串的 key 和实体类的成员变量. 例如,我们定义一个 POJO 类型的 User 实体类: public class User { public String firstName; public String lastName; pub

Fresco 源码浅析

前言 fresco是facebook主导的一个开源图片缓存库,从它提供的示例程序comparison来看,fresco貌似比其他的几个目前android主流的图片缓存库(glide,picasso,uil等)更快更节省内存.接下来就看看它到底是如何做到的. 一.背景: 1:lru与SoftReference那些年的爱恨情仇:很久很久以前,android的内存缓存还用SoftReference, 在2.3以后,垃圾回收机制被修改,软引用已不推荐用作缓存技术了,lru等才是正义.既然软引用已经不再推

Mahout推荐系统引擎UserCF中的IRStats部分源码解析

Mahout提供推荐系统引擎是模块化的,分为5个主要部分组成: 1. 数据模型 2. 相似度算法 3. 近邻算法 4. 推荐算法 5. 算法评分器 今天好好看了看关于推荐算法以及算法评分部分的源码. 以http://blog.csdn.net/jianjian1992/article/details/46582713 里边数据的为例进行实验. 整体流程的代码如下,依照上面的5个模块,看起来倒是很简单呀. public static RecommenderBuilder userRecommend