美文网首页
flutter 图片大小治理

flutter 图片大小治理

作者: 余瑜雨鱼 | 来源:发表于2020-11-03 14:11 被阅读0次

    本文主要介绍如何降低flutter图片资源占用的包大小

    第一个,最简单的是图片压缩,推荐这个png图片压缩

    第二是本文的重点,降低图片数量,使得每张图只需要放1张。

    flutter的本地图片和native的一样,默认情况下都会有1x 2x 3x,这也是官方推荐的,通过看源码,发现可以把3x的图放到外层,不需要往2x 3x文件夹里放图片,这样就可以了,只需要一份3x图,包大小能降低不少,接下来看源码。看看为什么这样是可行的

    假设项目里配置是这样的

    assets
        images
            only1.png
            only2.png
        2.0x
            only1.png
            only3.png
        3.0x
            only1.png
    

    先抛结论

    假设有如下代码,设备是3x,那最终加载的就是3.0x下的图片,优先选择最符合条件的

    Image.asset("assets/images/only1.png")

    假设有如下代码,设备是3x,那最终加载的就是最外层的图片,因为2x 和3x文件夹里都没有

    Image.asset("assets/images/only2.png")

    假设有如下代码,设备是3x,那最终图片是加载不出来的,因为最外层没有这张图

    Image.asset("assets/images/only3.png")

    Image.asset(
      String name, {
        ...
    }) : image = ResizeImage.resizeIfNeeded(cacheWidth, cacheHeight, scale != null
           ? ExactAssetImage(name, bundle: bundle, scale: scale, package: package)
           : AssetImage(name, bundle: bundle, package: package) //省略部分代码,scale不为null,走到AssetIamge,接着看
         ),
    

    AssetImage,注意obtainKey方法,这个方法的左右就是根据原始的key和设备的分辨率计算出最适合的图片key

    @override
    Future<AssetBundleImageKey> obtainKey(ImageConfiguration configuration) {
      final AssetBundle chosenBundle = bundle ?? configuration.bundle ?? rootBundle;
      Completer<AssetBundleImageKey> completer;
      Future<AssetBundleImageKey> result;
        //manifest 是最外层图片的名称的集合,在当前配置下,得到的map就是。所以1x也就是最外层一定要有图片的声明
        //{"only1.png",["assets/images/only1.png","assets/images/2.0x/only1.png","assets/images/3.0x/only1.png"]}
        //{"only2.png",["assets/images/only2.png"]}
      chosenBundle.loadStructuredData<Map<String, List<String>>>(_kAssetManifestFileName, _manifestParser).then<void>(
        (Map<String, List<String>> manifest) {
        // _chooseVariant就是根据设备信息,图片名称选出最适合的路径 // 假设3x设备,3x文件夹下有这张图,那结果就是3x下的图
          final String chosenName = _chooseVariant(
            keyName,
            configuration, // 图片名称 configuration, // 设备信息: 分辨率,语言等
            manifest == null ? null : manifest[keyName], //根据对应的key取出list
          );
        // 根据取得的图片路径 算出当前图片的缩放是1x 2x 还是3x
          final double chosenScale = _parseScale(chosenName);
          final AssetBundleImageKey key = AssetBundleImageKey(
            bundle: chosenBundle,
            name: chosenName,
            scale: chosenScale,
          );
          ...//省略部分代码
      ).catchError((dynamic error, StackTrace stack) {
        ...//省略部分代码
      });
      if (result != null) {
        return result;
      }
      completer = Completer<AssetBundleImageKey>();
      return completer.future;
    }
    

    关键还是_chooseVariant方法

    // main 原始图片路径
    // config 设备信息
    // candidates 当前图片路径下的集合 
    // 解析后的数据如下 ["assets/images/only1.png","assets/images/2.0x/only1.png","assets/images/3.0x/only1.png"]
    String _chooseVariant(String main, ImageConfiguration config, List<String> candidates) {
      if (config.devicePixelRatio == null || candidates == null || candidates.isEmpty)
        return main;
      
      final SplayTreeMap<double, String> mapping = SplayTreeMap<double, String>();
    // 遍历candidates,解析其中的缩放,缓存到mapping里,这是treemap 
      for (final String candidate in candidates)
        mapping[_parseScale(candidate)] = candidate;
       // 根据设备信息寻找最符合条件的图片
      return _findNearest(mapping, config.devicePixelRatio);
    }
     
    // candidates 是个treemap, 假设加载的only1.png,那candidates的结构如下
    //         3.0:3.0x/only1.png
    //  left  2.0:2.0x/only1.png  right null
    //  left  1.0:1.0x/only1.png  right null
    // value 是3.0 ,因为设备的分辨率是3.0
    String _findNearest(SplayTreeMap<double, String> candidates, double value) {
        // map里刚好以后3.0x的value,直接返回
      if (candidates.containsKey(value))
        return candidates[value];
        
        // 找到比value小的可以 ,最近的,假设设备是3.0,但最大的图是2x,那lower就是2.0
      final double lower = candidates.lastKeyBefore(value);
        // 找到第一个比value大的key,所以可以新建4.0 5.0的文件夹 放4x,5x的图都是可以的
      final double upper = candidates.firstKeyAfter(value);
      // 如果没有更低的,直接返回最大的
      if (lower == null)
        return candidates[upper];
      // 如果没有返回更大的,直接去最小的
      if (upper == null)
        return candidates[lower];
        // 用中间值做比较
      if (value > (lower + upper) / 2)
        return candidates[upper];
      else
        return candidates[lower];
    }
    

    至此,根据设备信息查找符合条件图片的源码分析完毕,结论

    • 1x 也就是最外层目录下 ,一定要有图片的声明,如果1x目录下没有声明图片,那即使2x,3x下有,AssertImage也不会识别
    • AssertImage 优先会取最符合当前设备分辨率的图片,如果是3.0的手机,优先取3.0 ,如果没有,取2.0,然后才是1.0
    • 综上所述,1x下放3x图片是可以的,比较现在市面上Android机绝大部分都是3x了,ios可能还会部分有2x,但也不会多了

    相关文章

      网友评论

          本文标题:flutter 图片大小治理

          本文链接:https://www.haomeiwen.com/subject/utubvktx.html