flutter 图片大小治理

本文主要介绍如何降低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,但也不会多了
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343