数据源Future, 有些相似,都是异步计算的结果。

不同点在于,数据源对于一个调用会返回一系列结果,Future只返回一个。

提交一个Image request之后,Image pipeline返回一个数据源。从中获取数据需要使用数据订阅者(DataSubscriber)

执行器(Executor)

当你订阅一个数据源时,必须制定一个Executor去执行。它主要是为了让你在制定的线程调度机制上执行 Runnable。Fresco提供了一些Executors,你在使用它们的时候需要注意:

  • 如果你想要在回调中进行任何UI操作,你需要使用UiThreadImmediateExecutorService.getInstance()。因为Android系统仅允许在UI线程中做一些UI操作。
  • 如果回调里面做的事情比较少,并且不涉及UI,你可以使用CallerThreadExecutor.getInstance()。这个 Executor 会在调用者线程中执行回调。这个回调的执行线程是得不到保证的,所以需要谨慎使用这个Executor。重申一遍,只有少量工作、没有UI操作的回调才适合在这个Executor中操作。
  • 你需要做一些比较复杂、耗时的操作,并且不涉及UI(如数据库读写、文件IO),你就不能用上面两个Executor。你需要开启一个后台Executor,可以参考DefaultExecutorSupplier.forBackgroundTasks

从数据源中获取结果

这里展示了从数据源中获取目标类型为TCloseableReference<T>的例子,注意此时获取结果对象仅仅在onNewResultImpl有使用意义。当回调结束之后,这个对象就不能被使用了!(如果你希望保持这个对象并在外部使用,请继续往下看)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    DataSource<CloseableReference<T>> dataSource = ...;

    DataSubscriber<CloseableReference<T>> dataSubscriber =
        new BaseDataSubscriber<CloseableReference<T>>() {
          @Override
          protected void onNewResultImpl(
              DataSource<CloseableReference<T>> dataSource) {
            if (!dataSource.isFinished()) {
              return;
            }
            CloseableReference<T> ref = dataSource.getResult();
            if (ref != null) {
              try {
                T result = ref.get();
                ...
              } finally {
                CloseableReference.closeSafely(ref);
              }
            }
          }

          @Override
          protected void onFailureImpl(DataSource<CloseableReference<T>> dataSource) {
            Throwable t = dataSource.getFailureCause();
            // handle failure
          }
        };

    dataSource.subscribe(dataSubscriber, executor);

保持数据源结果的引用

上面的例子中我们在回调执行完就将对象释放了。如果你需要保持这个对象有效,那么你可以不在这里close它,参照如下例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    DataSource<CloseableReference<T>> dataSource = ...;

    DataSubscriber<CloseableReference<T>> dataSubscriber =
        new BaseDataSubscriber<CloseableReference<T>>() {
          @Override
          protected void onNewResultImpl(
              DataSource<CloseableReference<T>> dataSource) {
            if (!dataSource.isFinished()) {
              return;
            }
            mRef = dataSource.getResult();
            T result = mRef.get();
            ...
          }

          @Override
          protected void onFailureImpl(DataSource<CloseableReference<T>> dataSource) {
            Throwable t = dataSource.getFailureCause();
            // handle failure
          }
        };

    dataSource.subscribe(dataSubscriber, executor);

你必须要在使用完它之后对它回收,否则会造成内存泄漏!

参考可关闭的引用 来获取更多信息。

1
2
    CloseableReference.closeSafely(mRef);
    mRef = null;

获取未解码的图片

1
2
    DataSource<CloseableReference<PooledByteBuffer>> dataSource =
        mImagePipeline.fetchEncodedImage(imageRequest, CALLER_CONTEXT);

Image pipeline 使用 PooledByteBuffer 来存储未解码的图片。 此处我们继续使用上面的目标类型 T来举个例子, 通过创建InputStream 来读取图片字节流:

1
2
3
4
5
6
7
8
9
      InputStream is = new PooledByteBufferInputStream(result);
      try {
        ImageFormat imageFormat = ImageFormatChecker.getImageFormat(is);
        Files.copy(is, path);
      } catch (...) {
        ...
      } finally {
        Closeables.closeQuietly(is);
      }

获取已解码的图片

1
2
DataSource<CloseableReference<CloseableImage>>
    dataSource = imagePipeline.fetchDecodedImage(imageRequest, callerContext);

Image pipeline CloseableImage 来承载已解码的图片。下面例子描述如何从CloseableImage拿出一个Bitmap对象:

1
2
3
4
5
6
	CloseableImage image = ref.get();
	if (image instanceof CloseableBitmap) {
	  // do something with the bitmap
	  Bitmap bitmap = (CloseableBitmap image).getUnderlyingBitmap();
	  ...
	}

只想要Bitmap不想要别的…

如果你向ImagePipeline请求一个Bitmap, 你可以使用我们的 BaseBitmapDataSubscriber:

1
2
3
4
5
6
7
8
9
10
11
dataSource.subscribe(new BaseBitmapDataSubscriber() {
    @Override
    public void onNewResultImpl(@Nullable Bitmap bitmap) {
      // 你可以直接在这里使用Bitmap,没有别的限制要求,也不需要回收
    }

    @Override
    public void onFailureImpl(DataSource dataSource) {
    }
  },
  executor);

注意,这里有一些要求!

  • 这个数据源无法用来获取动图。
  • 你无法在onNewResultImpl之外的地方使用这个Bitmap。原因是BaseBitmapDataSubscriber数据源的获取结束之后,image pipeline就会回收这个Bitmap。如果你此时再用它来显示,会报IllegalStateException
  • 当然你可以将它传给Android的通知栏或者[RemoveView]remote view。Android系统会在共享内存区域保存一份Bitmap的拷贝,Fresco的回收不会影响它。

如果这些要求导致你不能使用BaseBitmapDataSubscriber,你可以使用上述的其他数据源。

编辑和纠错