移动端对大批量图片加载的优化方法(三)

news2024/11/15 19:35:57

移动端对大批量图片加载的优化方法(三)Flutter

本篇主要从Flutter开发中可以使用到的对大批量图片加载的优化方法进行整理。优化

1.合适的图片格式

详情请参考移动端对大批量图片加载的优化方法(一)。

2.缓存机制

在Flutter中,处理大批量图片加载的缓存机制主要是依赖于第三方库和Flutter自身的架构;

a.缓存库

Glide和CacheableImage等可以自动处理图片的缓存和加载,使你能够更专注于应用的其他部分。

Future<void> _cacheImage() async {  
    // 指定要缓存的图片URL或路径  
    String imageUrl = 'https://example.com/image.jpg';  
    // 使用Glide加载图片并缓存到磁盘中  
    await Glide.with(context)  
        .load(imageUrl)  
        .into(Image.network(imageUrl)); // 将图片设置到Image widget中  
  } 
Future<void> _cacheImage() async {  
    // 指定要缓存的图片URL或路径  
    String imageUrl = 'https://example.com/image.jpg';  
    // 使用CacheableImage加载图片并缓存到磁盘中  
    await CacheableImage.network(imageUrl).cache(); // 缓存图片到磁盘中  
  }  
b.自定义缓存策略

不能使用第三方库,或者需要更精细的控制,可以创建一个自定义的缓存策略;
可以使用一个持久化的存储(如SQLite或SharedPreferences)来存储已经加载过的图片,并在需要时从缓存中检索它们;

Future<void> _cacheImage() async {  
    // 指定要缓存的图片URL或路径  
    String imageUrl = 'https://example.com/image.jpg';  
    // 加载图片并将其转换为字节数组  
    final response = await http.get(imageUrl);  
    final imageBytes = response.bodyBytes;  
    // 将字节数组写入SharedPreferences中作为二进制数据  
    SharedPreferences prefs = await SharedPreferences.getInstance();  
    prefs.setInt('image_length', imageBytes.length);  
    prefs.setInt('image_data', imageBytes.hashCode); // 使用哈希值作为键来存储二进制数据  
  }  

注意:可以但不推荐,SharedPreferences一般存储小量数据。

c.Flutter的缓存机制

Flutter本身提供了一些机制来管理资源,包括图片;
可以使用Flutter的AssetBundle API来控制图片的加载和缓存;
这个API允许从资源文件中加载图片,并可以在应用重启后保持这些图片的持久化。

Future<Image> loadImageFromAssetBundle() async {  
    final AssetBundle bundle = await AssetBundle.fromBundle(context);  
    final Uint8List imageBytes = await bundle.loadBytes('assets/image.jpg');  
    final Image image = Image.memory(imageBytes);  
    return image;  
  }  

3.异步加载

处理大量图片的异步加载是一个重要的任务,因为图片加载可能会阻塞UI线程,导致应用界面的卡顿;

a.FutureBuilder

FutureBuilder是一个构建器,用于处理Future。

FutureBuilder<List<dynamic>>(  
  future: fetchImages(), // 假设fetchImages是一个返回Future<List<dynamic>>的函数  
  builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {  
    if (snapshot.connectionState == ConnectionState.waiting) {  
      return CircularProgressIndicator(); // 加载中的UI  
    } else if (snapshot.hasError) {  
      return Text('Error: ${snapshot.error}');  
    } else {  
      return ListView(children: snapshot.data.map((image) => Image.network(image)).toList());  
    }  
  },  
);
b.异步函数

使用async和await关键字来编写异步函数,并在函数内部执行图片加载操作。

Future<void> loadImages() async {  
  List<dynamic> images = await fetchImages(); // 假设fetchImages是一个返回Future<List<dynamic>>的函数  
  // 处理加载完成的图片列表  
}
c.comet_engine

使用Comet引擎库来异步加载图片。

Future<Image> loadImage() async {  
    String imageUrl = 'https://example.com/image.jpg'; // 替换为你要加载的图片URL  
    CometTask task = _engine.getTask(CometRequestType.networkImage, imageUrl); // 创建网络图片请求任务  
    await task.waitForFinish(); // 等待任务完成并获取图片数据  
    return Image.memory(task.result.bodyBytes, imageUrl); // 创建Image对象并返回  
  }  

4.懒加载

对于列表或滚动视图中的图片,可以使用懒加载技术,只在需要显示时才加载图片。

ListView(  
        itemCount: imageUrls.length,  
        itemBuilder: (context, index) {  
          return Visibility(  
            visible: imageLoaded[index], // 假设imageLoaded是一个bool类型的列表,用于记录图片是否已加载完成  
            child: Image.network(imageUrls[index]), // 加载图片的URL或路径  
            onLoaded: () { // 图片加载完成后的回调函数  
              setState(() { // 更新UI状态时需要调用setState函数,确保UI的重新构建和更新  
                imageLoaded[index] = true; // 将对应图片的状态设置为已加载完成  
              });  
            },  
          );  
        }

5.控制图片大小

根据需要显示的大小加载图片,避免加载过大的图片。

Image(  
  image: AssetImage('assets/images/my_image.jpg'), // 加载图片资源  
  fit: BoxFit.cover, // 控制图片大小的方式,这里使用cover方式,即保持图片的纵横比并填充整个约束器  
)

6.优化内存管理

a.清理不再需要的图片

当图片不再需要时,及时清理它们以释放内存;
可以在图片不再可见时销毁对应的组件或使用Flutter的垃圾回收机制来清理不再使用的对象;

Image image = Image.network('https://example.com/image.jpg');  
// ... 显示图片 ...  
// 当图片不再需要时  
image.dispose();
// 清除所有缓存的图片  
ImageCache.clear();

或者使用Offstage或Visibility来控制图片的显示,当图片不再需要显示时,可以将其移到Offstage或隐藏起来;
这不会立即释放资源,但可以避免在不需要时显示不必要的图片;
当用户导航离开某个页面时,可能需要清理该页面加载的图片资源。

b.使用低质量预览图

在加载高质量图片之前,可以先显示一个低质量的预览图;
可以快速看到图片的大致内容,同时避免了长时间的等待和内存占用;
以下是使用image_utils生成预览图的示例:

// 加载原始图片  
  Uint8List imageBytes = ...; // 获取图片的字节数据  
  Image originalImage = Image.fromBytes(imageBytes);  
  
  // 降低图像质量  
  int quality = 50; // 设置质量等级,范围为0-100  
  Image lowQualityImage = originalImage.scale(quality);  
c.限制同时加载的图片数量

限制同时加载的图片数量,例如使用队列或优先级队列来管理图片的加载顺序。

class ImageLoader {  
  Queue<Future<void>> _queue = Queue<Future<void>>();  
  int _maxSimultaneousLoads = 3; // 设置并发加载的最大数量  
  
  Future<void> loadImage(String url) async {  
    if (_queue.length < _maxSimultaneousLoads) {  
      _queue.add(loadImageInternal(url));  
    } else {  
      // 等待当前队列中的图片加载完成后再加载新图片  
      await Future.wait(_queue);  
      _queue.add(loadImageInternal(url));  
    }  
  }  
  
  Future<void> loadImageInternal(String url) async {  
    // 加载图片的逻辑...  
    // 假设这里使用Image.network加载图片  
    await Image.network(url);  
  }  
}

7.预加载

在用户请求图片之前预先加载它们,从而提高图片加载速度和响应性。

class PreloadedImage extends StatefulWidget {  
  final String imageUrl;  
  final int fetchDelay; // 延迟时间(毫秒)  
  final bool useCache; // 是否使用缓存  
  
  PreloadedImage({required this.imageUrl, this.fetchDelay = 500, this.useCache = true});  
  
  @override  
  _PreloadedImageState createState() => _PreloadedImageState();  
}  
  
class _PreloadedImageState extends State<PreloadedImage> {  
  late Future<Uint8List> _futureImage;  
  late Image _imageWidget;  
  
  @override  
  void initState() {  
    super.initState();  
    _futureImage = Future<Uint8List>.delayed(Duration(milliseconds: widget.fetchDelay), () async {  
      return await http.get(widget.imageUrl).then((response) => response.bodyBytes);  
    });  
  }  
  
  @override  
  Widget build(BuildContext context) {  
    if (_futureImage != null && !_futureImage.done) {  
      return CircularProgressIndicator(); // 显示加载指示器  
    } else if (_futureImage == null) {  
      return Text('Image not loaded'); // 初始状态  
    } else {  
      final Uint8List imageBytes = _futureImage.result;  
      final ImageProvider imageProvider = Image.memory(imageBytes);  
      _imageWidget = Image(image: imageProvider,); // 使用图片数据构建图像Widget  
      return _imageWidget; // 显示图像Widget  
    }  
  }  
}

8.合适的View类型

ListView:当有大量可滚动的内容,并且每个项都是一个图片时,使用ListView是最佳选择;
它可以有效地重用子项,减少内存占用,并且可以水平或垂直滚动。
GridView:如果需要显示一个网格布局的图片,并且图片数量是动态的,可以使用GridView;
每个格子中可以放置一个小图片或缩略图。
Stack & Positioned:当需要动态创建大量浮动的图片层时,可以使用Stack和Positioned;
每个Positioned都可以放置一个图片,并且可以设置不同的位置和大小。
Image Widgets:直接使用Image小部件来显示单张大图或一组小图;
对于单张大图,可以使用Image.network或Image.asset来加载;
对于一组小图,可以使用ListView.builder结合Image.network或Image.asset来构建。
Cached Image:使用第三方库如cached_network_image来缓存网络图片,提高加载速度和性能;
这个库提供了预加载和缓存机制,非常适合加载大量图片。
FittedBox or Container with Fit.xxx:当需要精确控制图片的尺寸或者进行一些自定义布局时,可以使用FittedBox或Container的fit属性;
这样可以确保图片不会超出预期尺寸,从而优化内存使用。

9.优化网络请求

详情请参考移动端对大批量图片加载的优化方法(二)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1368967.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年山东省职业院校技能大赛高职组信息安全管理与评估—内存取证解析

内存取证 1、从内存中获取到用户admin的密码并且破解密码,以Flag{admin,password} 形式提交(密码为 6 位); volatility -f 1.vmem imageinfo 操作系统我们一般取第一个就可以了

vulhub中的Apache SSI 远程命令执行漏洞

Apache SSI 远程命令执行漏洞 1.cd到ssi-rce cd /opt/vulhub/httpd/ssi-rce/ 2.执行docker-compose up -d docker-compose up -d 3.查看靶场是否开启成功 dooker ps 拉取成功了 4.访问url 这里已经执行成功了&#xff0c;注意这里需要加入/upload.php 5.写入一句话木马 &…

Qt实现简单的分割窗口

最近在学习一些关于Qt的新知识&#xff0c;今天来讲述下我学习到的窗口分割&#xff0c;如果有不正确的&#xff0c;大家可以指正哦~ 首先&#xff0c;先看一下实现之后的简单效果吧&#xff01;省的说的天花乱坠&#xff0c;大家却不知道说的是哪个部分。 功能实现 整体demo…

阿里云RDMA通信库XRDMA论文详解

RDMA(remote direct memory access)即远端直接内存访问&#xff0c;是一种高性能网络通信技术&#xff0c;具有高带宽、低延迟、无CPU消耗等优点。RDMA相比TCP在性能方面有明显的优势&#xff0c;但在编程复杂度上RDMA verbs却比TCP socket复杂一个数量级。 开源社区和各大云厂…

如何在iOS手机上查看应用日志

引言 在开发iOS应用过程中&#xff0c;查看应用日志是非常重要的一项工作。通过查看日志&#xff0c;我们可以了解应用程序运行时的状态和错误信息&#xff0c;帮助我们进行调试和排查问题。本文将介绍两种方法来查看iOS手机上的应用日志&#xff0c;并提供相应的操作步骤。 …

Python学习之路——文件部分【书接上回】

一、书接上回 上个博客我说过&#xff0c;为什么最开始的时候一定要将文件内的中文的逗号替换为英文的逗号&#xff0c;接下来&#xff0c;请看&#xff08;其实想一想&#xff0c;感觉没必要&#xff0c;不过也是好的&#xff0c;总要练练手的嘛&#xff09; def func03(st…

Spring——基于注解的AOP配置

基于注解的AOP配置 1.创建工程 1.1.pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"…

基于DNA的密码学和隐写术综述

摘要 本文全面调研了不同的脱氧核糖核酸(DNA)-基于密码学和隐写术技术。基于DNA的密码学是一个新兴领域,利用DNA分子的大规模并行性和巨大的存储容量来编码和解码信息。近年来,由于其相对传统密码学方法的潜在优势,如高存储容量、低错误率和对环境因素的抗性,该领域引起…

怎么把图片表格转成word表格?教你简单转换

怎么把图片表格转成word表格&#xff1f;在我们的日常工作中&#xff0c;经常会遇到需要将图片表格转换成Word表格的情况。这样不仅可以方便我们编辑、整理数据&#xff0c;还能提高工作效率。那么&#xff0c;如何将图片表格快速、准确地转换成Word表格呢&#xff1f;下面就为…

openGauss学习笔记-190 openGauss 数据库运维-常见故障定位案例-服务启动失败

文章目录 openGauss学习笔记-190 openGauss 数据库运维-常见故障定位案例-服务启动失败190.1 服务启动失败190.1.1 问题现象190.1.2 原因分析190.1.3 处理办法 openGauss学习笔记-190 openGauss 数据库运维-常见故障定位案例-服务启动失败 190.1 服务启动失败 190.1.1 问题现…

国芯科技荣膺高工智能汽车“年度车规MCU高成长供应商”,加速产品精准化系列化布局

2023年12月13—15日&#xff0c;2023&#xff08;第七届&#xff09;高工智能汽车年会在上海召开&#xff0c;大会以“寻找拐点”为主题&#xff0c;通过超80场主题演讲及多场圆桌对话&#xff0c;为智能汽车赛道参与者「备战2024」提供全方位的决策支持。 作为汽车电子芯片领…

DNS解析原理和k8s DNS 实践

1. 问题背景 1.1 域名解析异常 近期开发的一个功能&#xff0c;需要在k8s集群容器环境中调用公司内部api&#xff0c;api提供了内网域名&#xff0c;解析内网域名异常导致请求超时&#xff0c;因此梳理了下DNS的知识点。 可以先看到下面&#x1f447;这段配置&#xff0c;修…

基于net6的asp.net core webapi项目打包为docker镜像,并推送至私有镜像仓库harbor中

基于net6的asp.net core webapi项目打包为docker镜像&#xff0c;并推送至私有镜像仓库harbor中 0、环境说明1、打包步骤1.1 创建Asp.net core WebApi项目1.2 在Asp.net core WebApi项目根目录下创建Dockerfile文件1.3 在子系统Ubuntu20.04.4中通过docker build生成docker镜像1…

资源分享:GIS插件-海怪工具箱(附下载链接)

工具详情&#xff1a; ArcGIS海怪工具箱&#xff0c;可直接在ArcGIS中安装使用。 功能&#xff1a; 1、按属性编码&#xff1a;根据字段编码&#xff0c;相同的字段值同一个编码&#xff0c;然后依次升序。 可以手动指定编码样式&#xff0c;比如输入1&#xff0c;那么后续编…

three.js实现旋转棱锥效果

three.js实现旋转棱锥效果 图例 步骤 创建一个四面棱锥添加贴图render函数中旋转&#xff0c;修改高度 代码 <template><div class"app"><div ref"canvesRef" class"canvas-wrap"></div></div> </template…

Python——欢迎来到吱昂张游乐园

欢迎来到吱昂张游乐园&#xff01;&#xff01;&#xff01; 凡是身高小于120或者您的vip等级大于三级的皆可免费游玩。 那我们接下来就来设计一下以上的规则叭 print("欢迎来到吱昂张游乐园") if int(input("输入您的身高&#xff1a;"))>120:print…

Python print 高阶玩法

Python print 高阶玩法 当涉及到在Python中使用print函数时&#xff0c;有许多方式可以玩转文本样式、字体和颜色。在此将深入探讨这些主题&#xff0c;并介绍一些print函数的高级用法。 1. 基本的文本样式与颜色设置 使用ANSI转义码 ANSI转义码是一种用于在终端&#xff0…

62.网游逆向分析与插件开发-游戏增加自动化助手接口-游戏公告类的C++还原

内容来源于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;游戏红字公告功能的逆向分析-CSDN博客 码云地址&#xff08;master分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;0888e34878d9e7dd0acd08ef…

HarmonOS 日期选择组件(DatePicker)

本文 我们一起来看基础组件中的 DatePicker 这就是 日程开发中的日期组件 他可以创建一个日期的范围 并创建一个日期的滑动选择器 这里 我们先写一个组件的骨架 Entry Component struct Index {build() {Row() {Column() {}.width(100%)}.height(100%)} }然后 我们先在Column组…

聚道云软件连接器助力某贸易公司实现付款流程自动化

客户介绍&#xff1a; 某贸易公司是一家集进出口贸易、国内贸易、电子商务等业务于一体的综合性贸易企业。公司业务遍及全球多个国家和地区&#xff0c;拥有庞大的供应商网络和采购需求。 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 客户痛点&#…