【问题标题】:Using Facebook's Fresco to load a bitmap使用 Facebook 的 Fresco 加载位图
【发布时间】:2015-04-06 17:55:16
【问题描述】:

我正在尝试用 Fresco 替换我的 android 应用中的 Picasso。但是我不确定如何使用 Fresco 简单地加载位图。

对于毕加索,我只会做以下事情。

Bitmap poster = Picasso.with(getActivity())
                    .load(url)
                    .resize(Utils.convertDpToPixel(WIDTH,HEIGHT))
                    .centerCrop()
                    .get();

我一直无法弄清楚如何使用此 Fresco 创建位图。有任何想法吗?

【问题讨论】:

  • AFAIK 因为 Fresco 不依赖于标准的 Bitmap 类,而是依赖于它们自己的实现,所以没有直接的方法来交换这个库。

标签: android facebook bitmap picasso fresco


【解决方案1】:

正如弗雷斯科所说:

如果您对管道的请求是解码图像 - Android 位图,您可以利用我们更易于使用的 BaseBitmapDataSubscriber:

dataSource.subscribe(new BaseBitmapDataSubscriber() {
    @Override
    public void onNewResultImpl(@Nullable Bitmap bitmap) {
       // You can use the bitmap in only limited ways
      // No need to do any cleanup.
    }

    @Override
    public void onFailureImpl(DataSource dataSource) {
      // No cleanup required here.
    }
  },
  executor);

您不能将位图分配给不在 onNewResultImpl 方法范围内的任何变量。

http://frescolib.org/docs/datasources-datasubscribers.html#_

我的代码:

public void setDataSubscriber(Context context, Uri uri, int width, int height){
    DataSubscriber dataSubscriber = new BaseDataSubscriber<CloseableReference<CloseableBitmap>>() {
        @Override
        public void onNewResultImpl(
                DataSource<CloseableReference<CloseableBitmap>> dataSource) {
            if (!dataSource.isFinished()) {
                return;
            }
            CloseableReference<CloseableBitmap> imageReference = dataSource.getResult();
            if (imageReference != null) {
                final CloseableReference<CloseableBitmap> closeableReference = imageReference.clone();
                try {
                    CloseableBitmap closeableBitmap = closeableReference.get();
                    Bitmap bitmap  = closeableBitmap.getUnderlyingBitmap();
                    if(bitmap != null && !bitmap.isRecycled()) {
                        //you can use bitmap here
                    }
                } finally {
                    imageReference.close();
                    closeableReference.close();
                }
            }
        }
        @Override
        public void onFailureImpl(DataSource dataSource) {
            Throwable throwable = dataSource.getFailureCause();
            // handle failure
        }
    };
    getBitmap(context, uri, width, height, dataSubscriber);
}

/**
 *
 * @param context
 * @param uri
 * @param width          
 * @param height         
 * @param dataSubscriber
 */
public void getBitmap(Context context, Uri uri, int width, int height, DataSubscriber dataSubscriber){
    ImagePipeline imagePipeline = Fresco.getImagePipeline();
    ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
    if(width > 0 && height > 0){
        builder.setResizeOptions(new ResizeOptions(width, height));
    }
    ImageRequest request = builder.build();
    DataSource<CloseableReference<CloseableImage>>
            dataSource = imagePipeline.fetchDecodedImage(request, context);
    dataSource.subscribe(dataSubscriber, UiThreadExecutorService.getInstance());
}

【讨论】:

  • 我们知道,resize选项只适用于jpeg,当涉及到png或gif之类的东西时,fresco不能完美地处理它。所以我只想自己调整位图的大小并通过 Fresco 加载它。但我不知道如何使用 fresco 加载位图而不是 uri。我想使用 Fresco 加载它的原因是使用它的缓存。
【解决方案2】:

你可以直接使用 Fresco 的 CacheKey:

public class DownloadVideoThumbnail extends AsyncTask<String, Void, Bitmap> {
private ImageView bmImage;
private Bitmap bitmapVideo;
private Context context;

public DownloadVideoThumbnail(Context context, ImageView bmImage) {
    this.bmImage = (ImageView) bmImage;
    this.context = context;
}

protected Bitmap doInBackground(String... urls) {

    String urlStr = urls[0];
    if (readFromCacheSync(urlStr) == null) {
        try {
            //Your method call here
            bitmapVideo = retriveVideoFrameFromVideo(urlStr);

        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    } else {
        bitmapVideo = readFromCacheSync(urlStr);
    }
    return null;
}

protected void onPostExecute(Bitmap result) {
    if (bitmapVideo != null) {
        //Load your bitmap here
        bmImage.setImageBitmap(bitmapVideo);
        bmImage.setScaleType(ImageView.ScaleType.CENTER_CROP);
    }
}


public void cacheBitmap(Bitmap bitmap, String url) {
    try {
        CacheKey cacheKey = new SimpleCacheKey(url);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        final byte[] byteArray = stream.toByteArray();
        Fresco.getImagePipelineFactory().getMainFileCache().insert(cacheKey, new WriterCallback() {
            @Override
            public void write(OutputStream outputStream) throws IOException {
                outputStream.write(byteArray);
            }
        });
    } catch (IOException cacheWriteException) {

    }
}

public static Bitmap readFromCacheSync(String imageUrl) {
    CacheKey cacheKey = DefaultCacheKeyFactory.getInstance().getEncodedCacheKey(ImageRequest.fromUri(imageUrl), null);
    StagingArea stagingArea = StagingArea.getInstance();
    EncodedImage encodedImage = stagingArea.get(cacheKey);
    if (encodedImage != null) {

        return BitmapFactory.decodeStream(encodedImage.getInputStream());
    }

    try {
        return BitmapFactory.decodeStream(readFromDiskCache(cacheKey));
    } catch (Exception e) {
        return null;
    }
}


private static InputStream readFromDiskCache(final CacheKey key) throws IOException {
    try {
        FileCache fileCache = ImagePipelineFactory.getInstance().getMainFileCache();
        final BinaryResource diskCacheResource = fileCache.getResource(key);
        if (diskCacheResource == null) {
            FLog.v(TAG, "Disk cache miss for %s", key.toString());
            return null;
        }
        PooledByteBuffer byteBuffer;
        final InputStream is = diskCacheResource.openStream();
        FLog.v(TAG, "Successful read from disk cache for %s", key.toString());
        return is;
    } catch (IOException ioe) {
        return null;
    }
}

public Bitmap retriveVideoFrameFromVideo(String videoPath) throws Throwable {

    Bitmap bitmap = null;
    MediaMetadataRetriever mediaMetadataRetriever = null;
    try {
        mediaMetadataRetriever = new MediaMetadataRetriever();
        if (Build.VERSION.SDK_INT >= 14)
            mediaMetadataRetriever.setDataSource(videoPath, new HashMap<String, String>());
        else
            mediaMetadataRetriever.setDataSource(videoPath);
        bitmap = mediaMetadataRetriever.getFrameAtTime();
        if (bitmap != null) {
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 70, stream);
            cacheBitmap(bitmap, videoPath);
        }
    } catch (Exception e) {
        e.printStackTrace();
        throw new Throwable(
                "Exception in retriveVideoFrameFromVideo(String videoPath)"
                        + e.getMessage());

    } finally {
        if (mediaMetadataRetriever != null) {
            mediaMetadataRetriever.release();
        }
    }
    return bitmap;
}
}

【讨论】:

  • 超级骗子工作..!
【解决方案3】:

您可以直接使用 Fresco 的图像管道:

http://frescolib.org/docs/using-image-pipeline.html

不过,如果您不介意我的提问 - 这里的动机是什么?为什么需要位图本身?

【讨论】:

  • 例如,您可能希望将图像保存在 SD 上并将其路径写入本地 SQLight 数据库。您可能还需要原始分辨率图像以供以后使用,而在上面的示例中调用 .resize() 会给您一个调整大小的图像(不确定 Fresco 是否会这样做,但这就是我在使用时自己调整图像大小的原因Picasso).
  • 我确信我会在我的新项目中尝试 Fresco,但只是指出 - 恕我直言,将下载的图像作为 Bitmap 对象访问(或您可以使用的其他格式直接操作)是许多开发人员可能希望看到的重要功能
  • 我需要为 Google Maps API 创建地图标记的参考(例如)
  • @Kenny 我也需要地图位图吗?你找到解决方案了吗?
【解决方案4】:

this 答案开始,我创建了一个使用 Kotlin 扩展和 RxJava 从ImageRequest 获取ClosableBitmap 的快速实现:

fun ImageRequest.getBitmap(context: Context): Maybe<CloseableReference<CloseableBitmap>> {
    val dataSource = Fresco.getImagePipeline().fetchDecodedImage(this, context)
    return Maybe.create { emitter ->
        dataSource.subscribe(
            object : BaseDataSubscriber<CloseableReference<CloseableImage>>() {
                override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
                    emitter.onComplete()
                }

                override fun onNewResultImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
                    if (!dataSource.isFinished) {
                        return
                    }

                    dataSource.result
                        ?.takeIf { it.get() is CloseableBitmap }
                        ?.let {
                            @Suppress("UNCHECKED_CAST")
                            emitter.onSuccess(it as CloseableReference<CloseableBitmap>)
                        }

                    emitter.onComplete()
                }
            },
            DefaultExecutorSupplier(1).forBackgroundTasks()
        )
    }
}

由于根据合同,一旦使用位图就需要关闭引用,所以我创建了这个 util 函数:

/**
 * The bitmap passed into [block] is only valid during the execution of the method.
 */
fun <T> CloseableReference<CloseableBitmap>.useBitmap(block: (Bitmap?) -> T): T? {
    return try {
        this.get()?.underlyingBitmap?.let { block(it) }
    } finally {
        CloseableReference.closeSafely(this)
    }
}

【讨论】:

    【解决方案5】:

    我使用 Kotlin 的协程找到了这个解决方案:

    suspend fun getBitmapFromUri(imageUri: Uri): Bitmap = withContext(Dispatchers.Default) {
        val imageRequest = ImageRequestBuilder.newBuilderWithSource(imageUri).build()
        val dataSource = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, this)
        val result = DataSources.waitForFinalResult(dataSource) as CloseableReference<CloseableBitmap>
    
        val bitmap = result.get().underlyingBitmap
    
        CloseableReference.closeSafely(result)
        dataSource.close()
    
        return@withContext bitmap
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-03
      • 1970-01-01
      • 2018-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多