flutter圖片內(nèi)存優(yōu)化

方法一

按照給定尺寸進(jìn)行圖片的解碼,而不是解碼整個(gè)圖片的尺寸,用來減少內(nèi)存的占用。

官方文檔:
https://api.flutter.dev/flutter/painting/ResizeImage-class.html

官方說明:
Instructs Flutter to decode the image at the specified dimensions instead of at its native size.

This allows finer control of the size of the image in ImageCache and is generally used to reduce the memory footprint of ImageCache.

The decoded image may still be displayed at sizes other than the cached size provided here.

使用:

Image(
                      image: ResizeImage(
                        NetworkImage('https://img-dev.xinxigu.com.cn/s1/2021/1/18/6e19c84b1b4aeb416bdee40615aa9854.jpg'),
                        width: AdaptUtils.pxW(150).toInt(),
                        height: AdaptUtils.pxW(150).toInt(),
                      ),
                    ),

方法二

三方庫:cached_network_image 限2.5.0之后版本才可用
設(shè)定最大的緩存寬度和高度this.maxWidthDiskCache、this.maxHeightDiskCache

image.png

  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,

使用:

CachedNetworkImage(
                      imageUrl: AppUtils.processImageUrl(url: coverUrl) ?? AppURL.defaultImageRectangle,
                      width: AdaptUtils.pxW(150),
                      height: AdaptUtils.pxW(150),
                      fit: BoxFit.cover,
                      placeholder: AppURL.placeholderRectangle(
                        width: AdaptUtils.pxW(150),
                        height: AdaptUtils.pxW(150),
                      ),
                      maxWidthDiskCache: AdaptUtils.pxW(150 * 2).toInt(),
                      maxHeightDiskCache: AdaptUtils.pxW(150 * 2).toInt(),
                    ),

方法三

從相冊(cè)選取圖片,展示時(shí)使用指定尺寸寬高進(jìn)行處理。
使用三方庫:

  # 仿微信資源選擇器
  wechat_assets_picker: ^4.2.0

  # 仿微信拍照
  wechat_camera_picker: ^1.2.1

使用自定義provider來指定所需圖片的寬高:

  /// The item builder for images and video type of asset.
  /// 圖片和視頻資源的部件構(gòu)建
  /// 縮略圖視圖
  static Widget thumbImageItemBuilder(
      BuildContext context,
      AssetEntity asset, // 圖片資源數(shù)據(jù)
      double thumbSizeWidth, // 縮略圖寬,同為圖片展示寬
      double thumbSizeHeight, // 縮略圖高,同為圖片展示高
      BoxFit fit, // 圖片展示方式
      ) {
    final AssetEntityImageProvider imageProvider = AssetEntityImageProvider(
      asset,
      isOriginal: false,
      thumbSize: [int.parse('${thumbSizeWidth.toStringAsFixed(0)}'), int.parse('${thumbSizeWidth.toStringAsFixed(0)}')],
    );
    return RepaintBoundary(
      child: Image(
        width: thumbSizeWidth,
        height: thumbSizeHeight,
        image: imageProvider,
        fit: fit,
      ),
    );
  }

AssetEntityImageProvider傳入寬高和圖片原圖AssetEntity數(shù)據(jù)。
providerkey.entity.thumbDataWithSize方法:

  Future<ui.Codec> _loadAsync(
    AssetEntityImageProvider key,
    DecoderCallback decode,
  ) async {
    assert(key == this);
    Uint8List data;
    if (isOriginal ?? false) {
      if (imageFileType == ImageFileType.heic) {
        data = await (await key.entity.file).readAsBytes();
      } else {
        data = await key.entity.originBytes;
      }
    } else {
      data = await key.entity.thumbDataWithSize(thumbSize[0], thumbSize[1]);
    }
    return decode(data);
  }

進(jìn)入entitythumbDataWithSize方法:

  /// get thumb with size
  Future<Uint8List> thumbDataWithSize(
    int width,
    int height, {
    ThumbFormat format = ThumbFormat.jpeg,
    int quality = 100,
  }) {
    assert(width > 0 && height > 0, "The width and height must better 0.");
    assert(format != null, "The format must not be null.");
    assert(quality > 0 && quality <= 100, "The quality must between 0 and 100");

    /// Return null if asset is audio or other type, because they don't have such a thing.
    if (type == AssetType.audio || type == AssetType.other) {
      return null;
    }

    return PhotoManager._getThumbDataWithId(
      id,
      width: width,
      height: height,
      format: format,
      quality: quality,
    );
  }

進(jìn)入_getThumbDataWithId方法中,

  static _getThumbDataWithId(
    String id, {
    int width = 150,
    int height = 150,
    ThumbFormat format = ThumbFormat.jpeg,
    int quality = 100,
  }) {
    return _plugin.getThumb(
      id: id,
      width: width,
      height: height,
      format: format,
      quality: quality,
    );
  }

進(jìn)入getThumb:

  Future<Uint8List> getThumb({
    @required String id,
    int width = 100,
    int height = 100,
    ThumbFormat format,
    int quality,
  }) {
    return _channel.invokeMethod("getThumb", {
      "width": width,
      "height": height,
      "id": id,
      "format": format.index,
      "quality": quality,
    });
  }

調(diào)用iOS原生的獲取圖片方法,

if ([call.method isEqualToString:@"getThumb"]) {
        NSString *id = call.arguments[@"id"];
        NSUInteger width = [call.arguments[@"width"] unsignedIntegerValue];
        NSUInteger height = [call.arguments[@"height"] unsignedIntegerValue];
        NSUInteger format = [call.arguments[@"format"] unsignedIntegerValue];
        NSUInteger quality = [call.arguments[@"quality"] unsignedIntegerValue];

        [manager getThumbWithId:id width:width height:height format:format quality:quality resultHandler:handler];

      }

進(jìn)入getThumbWithId方法,

- (void)getThumbWithId:(NSString *)id width:(NSUInteger)width height:(NSUInteger)height format:(NSUInteger)format quality:(NSUInteger)quality resultHandler:(ResultHandler *)handler {
  PMAssetEntity *entity = [self getAssetEntity:id];
  if (entity && entity.phAsset) {
    PHAsset *asset = entity.phAsset;
    [self fetchThumb:asset width:width height:height format:format quality:quality resultHandler:handler];
  } else {
    [handler replyError:@"asset is not found"];
  }
}

原生實(shí)現(xiàn)獲取置頂寬高縮略圖方法實(shí)現(xiàn):
使用iOS原生類PHImageManager

                     targetSize:CGSizeMake(width, height)
                    contentMode:PHImageContentModeAspectFill
                        options:options
                  resultHandler:^(UIImage *result, NSDictionary *info)

來獲取縮略圖。

- (void)fetchThumb:(PHAsset *)asset width:(NSUInteger)width height:(NSUInteger)height format:(NSUInteger)format quality:(NSUInteger)quality resultHandler:(ResultHandler *)handler {
  PHImageManager *manager = PHImageManager.defaultManager;
  PHImageRequestOptions *options = [PHImageRequestOptions new];
  [options setNetworkAccessAllowed:YES];
  [options setProgressHandler:^(double progress, NSError *error, BOOL *stop,
          NSDictionary *info) {
      if (progress == 1.0) {
        [self fetchThumb:asset width:width height:height format:format quality:quality resultHandler:handler];
      }
  }];
  [manager requestImageForAsset:asset
                     targetSize:CGSizeMake(width, height)
                    contentMode:PHImageContentModeAspectFill
                        options:options
                  resultHandler:^(UIImage *result, NSDictionary *info) {
                      BOOL downloadFinished = [PMManager isDownloadFinish:info];

                      if (!downloadFinished) {
                        return;
                      }

                      if ([handler isReplied]) {
                        return;
                      }
                      NSData *imageData;
                      if (format == 1) {
                        imageData = UIImagePNGRepresentation(result);
                      } else {
                        double qualityValue = (double) quality / 100.0;
                        imageData = UIImageJPEGRepresentation(result, qualityValue);
                      }

                      FlutterStandardTypedData *data = [FlutterStandardTypedData typedDataWithBytes:imageData];
                      [handler reply:data];
                  }];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容