在Fresco中缓存GIF

4

我正在尝试使用gif制作一个recyclerview。所有东西都显示得很完美,但fresco不能缓存gif。在向下滚动recycler并再次向上滚动后,gif再次加载。我想他们应该被缓存并且加载速度会更快一些。以前我使用过ION库。加载速度更快,而且没有缓存问题。我不得不更换库,因为它与gif解码有些问题,描述在这里。目前的解决方案看起来是这样的:

//for default initial in application class 

Fresco.initialize(this);
//I have also tried to change DiskCacheConfig and ImagePipelineConfig params. 
//Without any positive result

//for recyclerview on onBindViewHolder    
GenericDraweeHierarchy hierarchy = holder.draweeView.getHierarchy();
Uri uri = Uri.parse(path);
hierarchy.setPlaceholderImage(R.drawable.img_bg);
Logger.e(check(uri) + " " + uri.toString());
DraweeController controller = Fresco.newDraweeControllerBuilder().setUri(uri)
            .setAutoPlayAnimations(true).build();
    holder.draweeView.setController(controller);

//for method which show cached uri images in imagepipeline
public static boolean check(Uri uri) {
    ImagePipeline imagePipeline = Fresco.getImagePipeline();
    return imagePipeline.isInBitmapMemoryCache(uri);
}
//... all the time log shows "false + gif url"

我没有看到关于不缓存动画图片的任何信息。有关动画图像的不支持的后处理信息,但这就是全部内容。如何正确地缓存gif?

编辑: 看起来Fresco可以缓存动画,因为下面的方法对于重新加载的gif返回true。

public static boolean isImageDownloaded(Uri loadUri) {
    if (loadUri == null) {
        return false;
    }
    CacheKey cacheKey = DefaultCacheKeyFactory.getInstance()
            .getEncodedCacheKey(ImageRequest.fromUri(loadUri));
    return ImagePipelineFactory.getInstance().getMainDiskStorageCache().hasKey(cacheKey)
            || ImagePipelineFactory.getInstance().getSmallImageDiskStorageCache()
                    .hasKey(cacheKey);
}
3个回答

1

为了让事情更加清晰,Fresco有三个级别的缓存:

  • DiskCache - 保留图像文件的原始格式。(确切地说,在Android版本不完全支持webp图像的情况下,它们可能会在存储之前被转码为其他格式。)
  • EncodedMemoryCache - 图像的原始编码格式的内存缓存。(图像被保留为字节数组的原始字节,如它们在磁盘上存储的那样 + 一些附加元数据。)
  • BitmapMemoryCache - 主要由Android位图组成的内存缓存。位图是解码后的图像,每个像素占用32位,这比编码时所需的空间要大得多。

显然,这是空间与时间的权衡。可用内存有限,如果图像不在位图缓存中,则必须再次进行解码。此外,如果它也不在编码内存缓存中,则必须从磁盘中读取,这也可能很慢。

现在回到动画图像。这是一个已知的限制。动画图像不会以它们解码后的形式被缓存,因为那样会耗尽位图缓存(只需将num_frames * width * height * 32bpp相乘),并且单个动画图像可能会驱逐缓存中的每个其他图像。相反,它们按需解码,并且仅保留几个即将显示的帧在短暂的缓存中。
我们有一些改进动画的计划,但我不能提供任何时间估计。

1
我只想知道如何缓存并显示gif的第一帧。 - Allen Vork
你可能需要在原始问题中澄清,你只对第一帧感兴趣。 - plamenko

0

Fresco,它们主要专注于低大小的gif。我在recycleview中尝试了低大小的gif,缓存完美地工作。如果您使用高分辨率的gif,则需要高内存消耗进行解码和缓存。可能会超过堆大小。因此,它们是按需解码的,并且只有几个帧(即将显示的帧)被缓存。

可以配置Fresco在第一帧可用时立即显示第一帧(而不是解码整个帧),并轻松缓存静态第一帧。

        ImageDecodeOptionsBuilder b = new ImageDecodeOptionsBuilder();
        b.setForceStaticImage(true);
        ImageDecodeOptions imageDecodeOptions=new ImageDecodeOptions(b);
        ImageRequest request =  ImageRequestBuilder.newBuilderWithSource(animatedGifUri).setImageDecodeOptions(imageDecodeOptions).setLocalThumbnailPreviewsEnabled(true).build();

0

我遇到了同样的问题,看起来Fresco正确地缓存了gif图像,但实际上每次滚动RecyclerView或任何其他视图时,解码和播放动画都需要时间。 然而,如果您使用没有动画的gif图像,则在浏览视图时gif图像不会“重新加载”。

由于我可以控制应用程序中显示的图像。我在我的服务器上创建了2个版本的gif图像,第一个是没有动画的,用于在RecyclerView / ListView中显示,另一个用于在用户单击列表中的项目时在“媒体查看器”活动中显示。


这个解决方案并不令人满意。我需要展示一个自动启动的GIF网格。当你查看GIF键盘应用时,你会看到一个带有自动启动动画的GIF列表/网格。它可以平滑滚动,并具有可接受的缓存行为。他们的解决方案应该是合适的。 - BactaTank
很遗憾,我无法弄清他们如何实现高效缓存和流畅滚动。 - BactaTank
@McHaimech,你找到解决gif的2个版本的方法了吗? - Paraneetharan Saravanaperumal
setAutoPlayAnimations(false) 不解码 GIF 是个好主意。也许第一帧缩略图已经被缓存了。这是真的吗,@AllenVork? - Paraneetharan Saravanaperumal
@Nihilus13 使用Glide而非Fresco的GIF键盘 - Paraneetharan Saravanaperumal
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接