cached_network_image实践 2023-06-2

2023-06-20  本文已影响0人  松哥888

简介

Flutter提供的图片展示工具没有缓存功能,这个插件填补了空白。在Pub上,这个插件的评价也是很高的。

企业微信截图_369431f4-e23f-43ee-b7f8-38cba375e753.png

缓存

这个插件的缓存,调用了另外一个插件flutter_cache_manager

企业微信截图_38551c09-a849-4f72-952b-6d05fcdf016f.png

关于这一点,在cached_network_image的介绍中有提到,不过基本上很少有人注意。
大多数时候,感觉缓存应该是有的,访问过的大图,再次访问速度会比较快。只是平时用的时候根本不会去关心缓存的事情。基本上会按照官网介绍的用:

CachedNetworkImage(
        imageUrl: "http://via.placeholder.com/350x150",
        placeholder: (context, url) => CircularProgressIndicator(),
        errorWidget: (context, url, error) => Icon(Icons.error),
     ),

大部分时候,就是把placeholder换成一个本地图片,至于缓存,基本上不会在意。

由缓存导致的问题

class NetworkImageWidget extends StatelessWidget { 
    /// ... ...
    
    return CachedNetworkImage(
    imageUrl: actualUrl,
    placeholder: (context, url) => _buildPlaceholderImage(actualPlaceholder),
    errorWidget: (context, url, error) => _buildPlaceholderImage(actualPlaceholder),
    width: width,
    height: height,
  );
}

怎么会这样?怎么解决?在线等,挺急的……

默认缓存背锅

CachedNetworkImage({
    Key? key,
    required this.imageUrl,
    this.httpHeaders,
    this.imageBuilder,
    this.placeholder,
    this.progressIndicatorBuilder,
    this.errorWidget,
    this.fadeOutDuration = const Duration(milliseconds: 1000),
    this.fadeOutCurve = Curves.easeOut,
    this.fadeInDuration = const Duration(milliseconds: 500),
    this.fadeInCurve = Curves.easeIn,
    this.width,
    this.height,
    this.fit,
    this.alignment = Alignment.center,
    this.repeat = ImageRepeat.noRepeat,
    this.matchTextDirection = false,
    this.cacheManager,
    this.useOldImageOnUrlChange = false,
    this.color,
    this.filterQuality = FilterQuality.low,
    this.colorBlendMode,
    this.placeholderFadeInDuration,
    this.memCacheWidth,
    this.memCacheHeight,
    this.cacheKey,
    this.maxWidthDiskCache,
    this.maxHeightDiskCache,
    ImageRenderMethodForWeb imageRenderMethodForWeb =
        ImageRenderMethodForWeb.HtmlImage,
  })  : _image = CachedNetworkImageProvider(
          imageUrl,
          headers: httpHeaders,
          cacheManager: cacheManager,
          cacheKey: cacheKey,
          imageRenderMethodForWeb: imageRenderMethodForWeb,
          maxWidth: maxWidthDiskCache,
          maxHeight: maxHeightDiskCache,
        ),
        super(key: key);

缓存参数cacheManager,也被传给了CachedNetworkImageProvider

Stream<ui.Codec> _loadBufferAsync(
    image_provider.CachedNetworkImageProvider key,
    StreamController<ImageChunkEvent> chunkEvents,
    DecoderBufferCallback decode,
  ) {
    assert(key == this);
    return ImageLoader().loadBufferAsync(
      url,
      cacheKey,
      chunkEvents,
      decode,
      cacheManager ?? DefaultCacheManager(),
      maxHeight,
      maxWidth,
      headers,
      errorListener,
      imageRenderMethodForWeb,
      () => PaintingBinding.instance.imageCache.evict(key),
    );
  }
/// The DefaultCacheManager that can be easily used directly. The code of
/// this implementation can be used as inspiration for more complex cache
/// managers.
class DefaultCacheManager extends CacheManager with ImageCacheManager {
  static const key = 'libCachedImageData';

  static final DefaultCacheManager _instance = DefaultCacheManager._();
  factory DefaultCacheManager() {
    return _instance;
  }

  DefaultCacheManager._() : super(Config(key));
}

就是简单地把插件flutter_cache_manager封装成了单例,什么也没干,确实够懒的。注释也算实诚,“可以直接用,也可以提供一个灵感……”,这话讲的,真不好评价

import '_config_unsupported.dart'
    if (dart.library.html) '_config_web.dart'
    if (dart.library.io) '_config_io.dart' as impl;

abstract class Config {
  /// Config file for the CacheManager.
  /// [cacheKey] is used for the folder to store files and for the database
  /// file name.
  /// [stalePeriod] is the time duration in which a cache object is
  /// considered 'stale'. When a file is cached but not being used for a
  /// certain time the file will be deleted.
  /// [maxNrOfCacheObjects] defines how large the cache is allowed to be. If
  /// there are more files the files that haven't been used for the longest
  /// time will be removed.
  /// [repo] is the [CacheInfoRepository] which stores the cache metadata. On
  /// Android, iOS and macOS this defaults to [CacheObjectProvider], a
  /// sqflite implementation due to legacy. On web this defaults to
  /// [NonStoringObjectProvider]. On the other platforms this defaults to
  /// [JsonCacheInfoRepository].
  /// The [fileSystem] defines where the cached files are stored and the
  /// [fileService] defines where files are fetched, for example online.
  factory Config(
    String cacheKey, {
    Duration stalePeriod,
    int maxNrOfCacheObjects,
    CacheInfoRepository repo,
    FileSystem fileSystem,
    FileService fileService,
  }) = impl.Config;

  String get cacheKey;
  Duration get stalePeriod;
  int get maxNrOfCacheObjects;
  CacheInfoRepository get repo;
  FileSystem get fileSystem;
  FileService get fileService;
}

这个impl也不知道具体是个什么东西,姑且认为是整个Flutter运行环境吧,这样的话,随着图片的增长,这个缓存把整个Flutter内存都占了也是可能的

指定缓存解决

class CustomCacheManager {
  static const key = 'customCacheKey';
  static CacheManager instance = CacheManager(
    Config(
      key,
      stalePeriod: const Duration(days: 7),
      maxNrOfCacheObjects: 20,
      repo: JsonCacheInfoRepository(databaseName: key),
      fileSystem: IOFileSystem(key),
      fileService: HttpFileService(),
    ),
  );
} 
class NetworkImageWidget extends StatelessWidget { 
    /// ... ...

    return CachedNetworkImage(
    cacheManager: CustomCacheManager.instance,
    imageUrl: actualUrl,
    placeholder: (context, url) => _buildPlaceholderImage(actualPlaceholder),
    errorWidget: (context, url, error) => _buildPlaceholderImage(actualPlaceholder),
    width: width,
    height: height,
  );
}

class CustomCacheManager {
  static const key = 'image_cache_key';
  static CacheManager instance = CacheManager(
    Config(
      key,
      stalePeriod: const Duration(days: 7),
      maxNrOfCacheObjects: 100,
    ),
  );
}

100这个数字虽然是拍脑袋给的,不过事后来看还真的有点道理。每张照片大约2M,缓存100张,就是200M,占总量2G的十分之一,也算合理。

关于CacheManager

  /// Get the file from the cache.
  /// Specify [ignoreMemCache] to force a re-read from the database
  @override
  Future<FileInfo?> getFileFromCache(String key,
          {bool ignoreMemCache = false}) =>
      _store.getFile(key, ignoreMemCache: ignoreMemCache);

  ///Returns the file from memory if it has already been fetched
  @override
  Future<FileInfo?> getFileFromMemory(String key) =>
      _store.getFileFromMemory(key);
  /// 判断图片是否存在缓存中
  Future<bool> isPhotoInCache(String urlString) async {
    bool isIn = false;
    var fileInfo = await photoCache.getFileFromCache(urlString);
    if (fileInfo != null) {
      isIn = true;
      debugPrint('图片已经在缓存中,url:${fileInfo.originalUrl}');
    }
    return isIn;
  }
上一篇 下一篇

猜你喜欢

热点阅读