Open pranavkpr1 opened 4 years ago
Important update: I also tried using global cache manager with maxNumberOfObjects limited to some number which could be stored in cache. Then I shared this BaseCacheManager instance as parameter to all instances of CachedNetworkImage in my code as shown as follows:
class ImageCacheManager extends BaseCacheManager {
static const key = "MyApp_Cache"; //just use some unique name instead of MyApp
static ImageCacheManager _instance;
factory ImageCacheManager() {
if (_instance == null) {
instance = new ImageCacheManager.();
}
return instance;
}
ImageCacheManager.() : super(key,
maxAgeCacheObject: Duration(days: 1),
maxNrOfCacheObjects:20
);
Future
} } ImageCacheManager ImageCacheStorage=new ImageCacheManager();
CachedNetworkImage( imageUrl: "", placeholder: (context, url) => Image.memory(kTransparentImage), errorWidget: (context, url, error) => Image.memory(kTransparentImage), fadeInCurve: Curves.easeIn, memCacheWidth: 500, memCacheHeight:500, cacheManager: ImageCacheStorage, ) But still the memory used in the heap is increasing to 500MB-600MB which doesn't make any sense to me if I am limiting the maxNumber of objects stored in cache to 20.
Then I tried using Image.network with cacheWidth and cacheHeight parameters and setting imageCache size to 50MB as follows: imageCache.maximumSizeBytes=50000000; Network Image is behaving in correct way and heap size doesn't increase beyond 70-80 MB.
For now, I would prefer to use NetworkImage to avoid app crashes even if CachedNetworkImage has positive points such as errorWidget and placeholderWidget. I hope that the contributors could comment on the issue for how to use the CachedNetworkImage effectively in case of large number of images in the app without increasing the heap size to a large amount so that it can help me and other future developers to take the right decision :)
@pranavkpr1 _imageCache.clearLiveImages(); Can reduce a lot of memory
@pranavkpr1 _imageCache.clearLiveImages(); Can reduce a lot of memory
Hi @pushuhengyang , Thanks for the solution, but can you please help us where to use this code. We are also using cachednetwork image and facing the OOM in iOS (Low-end devices) mostly.
I am also experiencing extreme memory issues.
I prefer this library because it has fade in animations and provides good functionality, but it can easily bring my app to use over 1GB of RAM and beyond.
I have experienced the app using so much RAM that the OS starts killing off all background apps to eventually kill off my app in a last attempt to not die, because images are not freed. External memory just goes on to consume all available RAM.
My app allows accessing large images up to 8k etc and if the user loads a significant amount of them, this will lead to the app getting killed by the OS or like others mentioned, just crashing.
I understand that keeping images in RAM is important so they dont have to be loaded again and again and this is also very useful in the case of my app, but that there is literally no limit to RAM consumption is rather detrimental.
This is a rather big issue for me right now, and I would love it if there was a fix.
@pranavkpr1 _imageCache.clearLiveImages(); Can reduce a lot of memory
Hi @pushuhengyang , Thanks for the solution, but can you please help us where to use this code. We are also using cachednetwork image and facing the OOM in iOS (Low-end devices) mostly.
Judge every time you load the image
@pushuhengyang and where would you call _checkMemory()? Can you provide use with a minimal working example?
@pushuhengyang and where would you call _checkMemory()? Can you provide use with a minimal working example?
It's best to encapsulate a widget
Hi all. In version 2.3.1 there have been some improvements in memory usage. Next to that you can set the height and/or width of the image in memory using memCacheWidth/memCacheHeight. Setting these to a reasonable size should help a large part of the memory issues. https://pub.dev/documentation/cached_network_image/latest/cached_network_image/CachedNetworkImage-class.html
Hi Rene, As I mentioned in this post, I have already tried using dev version of CachedNetworkImage which is the new version you are asking to try. I have already tried adding memCacheWidth/ memCacheHeight as mentioned in the original post but still I was facing the same memory issue when I was performing memory profiling as shown in the attached screenshots in the original post. According to my experience, we can't use this package in e-commerce or social media kind of app where there is use case of showing large number of images as it can trigger crashes due to OOM issue. Hope to hear from you soon.
Sorry didn't read it good enough. I have to take some time to dig into this.
I am also facing the same issue. On the simulator, it just works and on the real iPhone, it crashes.
For the time being, I switched to https://github.com/humblerookie/optimized_cached_image which is very similar to flutter_cached_network_image and in fact, internally uses octo_image.
One more thing I noticed is when providing memCacheWidth/memCacheHeight in flutter_cached_network_image it stops crashing but flutter's ResizeImage cacheWidth/cacheHeight don't care about the image fit we mention for the CachedNetworkImage
Source https://github.com/flutter/flutter/issues/52802 https://github.com/flutter/flutter/pull/64352
@pushuhengyang and where would you call _checkMemory()? Can you provide use with a minimal working example?
It's best to encapsulate a widget
This does not work for me. I execute this code on every build of a CachedNetworkImage
print(_imageCache.liveImageCount);
print(_imageCache.currentSize);
print(_imageCache.currentSizeBytes);
This results in
flutter: 1
flutter: 1
flutter: 3616960
But my app still crashes. I wil now start with profiling to check why this is happening.
When profiling I see a very big spike in memory usage. I have the same problem with Image.network:
https://github.com/flutter/flutter/issues/47378#issuecomment-692060368
This is our workaround for the moment. We wrote our own widget that downscales and caches the downscaled image to the cache
for this we will need the devicePixelRatio but that is coming from MediaQuery.of so you will need to fetch that value before you are using this widget. (We save this value in our config when MaterialApp is build. so we can always access this because the devicePixelRatio is will not change. )
import 'dart:typed_data'; import 'dart:ui';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:pedantic/pedantic.dart'; import 'package:flutter/material.dart';
class MyProjectBetterImageWidget extends StatefulWidget { final String imageUrl; final BoxFit fit; final double width; final double height; final Widget placeholder;
const MyProjectBetterImageWidget({ @required this.imageUrl, @required this.fit, @required this.width, @required this.height, @required this.placeholder, Key key, }) : super(key: key);
@override _MyProjectBetterImageWidgetState createState() => _MyProjectBetterImageWidgetState(); }
class _MyProjectBetterImageWidgetState extends State
Uint8List _image;
Uint8List get image => _image;
bool get showPlaceholder => _hasError || _isLoading || _image == null;
@override void initState() { super.initState(); _getImage(); }
@override void didUpdateWidget(MyProjectBetterImageWidget oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.imageUrl != widget.imageUrl) { _getImage(); } }
Future
final width = (widgetWidth * FlavorConfig.instance.devicePixelRatio).toInt();
final height = (widgetHeight * FlavorConfig.instance.devicePixelRatio).toInt();
final url = '$originalUrl?w=$width&h=$height';
var fileInfo = await DefaultCacheManager().getFileFromCache(url);
var fromCache = true;
if (!mounted) return;
if (fileInfo == null) {
fromCache = false;
fileInfo = await DefaultCacheManager().downloadFile(url);
}
// ignore: invariant_booleans
if (!mounted) return;
if (fromCache) {
_image = await fileInfo.file.readAsBytes();
setState(() {
_isLoading = false;
_hasError = false;
});
return;
}
final bytes = await fileInfo.file.readAsBytes();
final codec = await instantiateImageCodec(
bytes,
targetWidth: width >= height ? width : null,
targetHeight: height > width ? height : null,
);
final frame = await codec.getNextFrame();
final data = await frame.image.toByteData(format: ImageByteFormat.png);
_image = data.buffer.asUint8List();
if (!fromCache) {
unawaited(_cacheImage(url));
}
} catch (e) {
MyProjectLogger.logError(message: 'Failed to parse image: $originalUrl', error: e);
_hasError = true;
} finally {
_isLoading = false;
}
// ignore: invariant_booleans
if (mounted) {
setState(() {});
}
}
Future
@override Widget build(BuildContext context) { if (showPlaceholder) return widget.placeholder; return Image.memory( image, width: widget.width, height: widget.height, fit: widget.fit, ); } }
Thanks for the workaround, @vanlooverenkoen! Are you using that in production now? No memory errors? I was trying to adapt it to my project and I have a couple of questions.
Questions:
errorWidget
. I can add it, maybe you evolved that already.Also, do you think this widget is relevant enough to move this to its own repo, so we don't divert the conversation from the main issue about flutter_cached_network_image
?
I've got the same issue, the app loading about 100s images then exit suddenly (randomly, sometimes it crashes on about load 20-30 images,sometimes 200+ images), I read the log, the message about not ensure folder exists /mnt/shell/emulated/0/Android/data/..../caches and /mnt/shell/emulated/0/Android/data/..../files
I've added read, write externalcard permission, and the app not crash and exit suddenly.
If I didnt add the permission, why the app still run and crash randomly, why the lib not require permission at the first run.
updated: my app still randomly crash
@renefloor adding the memCacheWidth/memCacheHeight do reduce the memory hike as I check in Memory dart tools. But setting this value causing the image to be blur. Or we can say it pixelate. Is there any solution for it?
@RaashVision at what value did you set it? If you set it really small I can imaging that it becomes pixelated. It is better to take other measures, for example that you don't show more than 10 images at the same time. One way is for example by using ListView.builder: https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html
I get the same problem too. I have a listview builder and hundreds of png images on it. But when app try to load listview with images and scroll on listview, memory goes up to 2GB. App starts 300MB of memory usage but when app try to load images, memory starts to increase, and on iphone 11 pro kills the app around 2gb of RAM usage, iphone 6 kills the app around 650 Mb of RAM usage. I tried various methods but somehow couldn't solve the memory problem.
CachedNetworkImage( imageUrl: image_url, placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Image.asset(noImage), ),
@halkportal970 Did you find any solution my case is the same as yours
@AdnanKazi Yes I found a solution. I added memCacheWidth to all CachedNetworkImage widgets. This realy reduced memory usage. Because my images width and height properties are very big. But this is not a very good solution. In web service side we must decrease images width and height according to mobile.
CachedNetworkImage(
imageUrl: image_url,
memCacheWidth: (Get.width * 0.6).toInt(),
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Image.asset(noImage),
),
@halkportal970 Thank you so much for your advice in the meantime I have also found the solution which doesn't crash the app
Image.network('url',
cacheHeight: widget.cacheHeight,
cacheWidth: widget.cacheWidth,
fit: BoxFit.cover,
errorBuilder: (context, url, error) => Icon(Icons.error),
)
I will also try your solution as well as your solution looks better one
Having the same issue using this plugin. It raises a EXC_RESOURCE RESOURCE_TYPE_MEMORY
on the thread that has name = io.flutter.1.io
.
@pranavkpr1 _imageCache.clearLiveImages(); Can reduce a lot of memory
It works!
I just tried an example with setting memCacheHeight
and for me it largely reduced the memory footprint of the app.
@pranavkpr1 Hi... Can you pls share an example how u solved this issue........ I am also facing the same issue
@gathodeharrkirat did you try setting memCacheHeight? For example:
CachedNetworkImage(
height: 200,
width: 200,
memCacheHeight: 200,
imageUrl: url,
),
I am currently debugging an app ready for production but getting reports of crashes on older iphones (1gb RAM). They are mostly caused by images not being correctly optimized by our client (like 4k pics for previews...). Limiting them with memCacheHeight and memCacheWidth should do the trick for most, but it's kind of a sledgehammer method. Source Images should be optimized, not cut like this, as this also stretches non fitting resolutions.
A good idea is also using your own global cache manager instance, listening to low memory warning notifications from the system and dumping the cache to prevent crashes.
This is overall a very classic "limitation" of GPU frameworks and game engines. Pictures and Text (which is rendered and uploaded as bitmaps) have to be highly optimized and manually controlled. A 1kb picture can easily take 20mb+ RAM. Look up the tech and math behind it if you professionally build apps with GPU frameworks!
Is it possible to add same memCacheHeight
option to CachedNetworkImageProvider
?
My usecase is that I want to preload some images before showing them and to do so I need ImageProvider
in precacheImage()
method.
Or maybe an option to get ImageProvider
from CachedNetworkImage
object as it's done in Image
class (image
property).
I was still getting issues with the app crashing when loading several (approximately 30) large (1-5 MB) images until I also set 'maxWidthDiskCache' and 'maxHeightDiskCache' (setting 'memCacheWidth' and 'memCacheHeight' was not enough).
I think I also had to ensure that the 'maxWidthDiskCache' and 'maxHeightDiskCache' values were the same as 'memCacheWidth' and 'memCacheHeight' respectively or else the memory cache was not used.
Does anyone know how I can verify that I have fixed this issue for good? I am worried the app will continue to crash on older devices or if I introduce more images.
I was still getting issues with the app crashing when loading several (approximately 30) large (1-5 MB) images until I also set 'maxWidthDiskCache' and 'maxHeightDiskCache' (setting 'memCacheWidth' and 'memCacheHeight' was not enough).
I think I also had to ensure that the 'maxWidthDiskCache' and 'maxHeightDiskCache' values were the same as 'memCacheWidth' and 'memCacheHeight' respectively or else the memory cache was not used.
Does anyone know how I can verify that I have fixed this issue for good? I am worried the app will continue to crash on older devices or if I introduce more images.
@gitaaron I'm not sure if this will help for your case. But I was facing a similar crash problem due to memory limit in the past with a lot of images ranging from 1mb - 4mb showing in a listview builder, even after setting up everything as suggested in the sub.
I ended up compressing all the images manually in the database to 70kb, and the crashing issues are gone.
So my assumptions is that images cannot be over 1mb when showing in a listview. 🤔
@renefloor Setting memCacheHeight is an effective way,but not wok on gif,any idea?
@weiminghuaa I can imagine that gifs are not resized. I have to study on how that could work. However, resizing a gif will probably mean that we will have to extract all images from the gif, resize them all and combine it again to a gif. I really recommend to resize gifs on the backend and return a smaller gif if that's possible.
I read code and saw that _memCache is Map without having any method to limit number of item cached on memory. Hence I think the memory size will increase until it reach the maximum allowance of system => app crashed
I think the memory issue of Image In ListView, maybe, because of 'addAutomaticKeepAlives' of ListView, setting false could sovle the problem.
I was experiencing a lot of memory issues with a gallery builder that loads up to 60 images using cachednetworkimage package.
I could solve my memory problems with:
Inserting this piece of code in my dispose method:
` @override
void dispose() {
_scrollController.dispose();
ImageCache _imageCache = PaintingBinding.instance!.imageCache!;
_imageCache.clear();
_imageCache.clearLiveImages();
super.dispose();
}`
https://github.com/Baseflow/flutter_cached_network_image/issues/429#issuecomment-1121360368 Good point but it doesn't work 😢 Is there any solution, my app still crashed with huge memory? 😭
This is an ongoing issue on our end as well, but our image sizes are fairly small. This primarily occurs on ios devices actually. Our Android tests have shown no crashes, while ios crashes due to this package.
We are also facing this issue, similar performance degradation on both ios and android. The image sizes are fairly reasonable. Is there some file format that has a lower memory footprint that we can use?
Facing this issue as well with Flutter 3.7
Downgrading to Flutter 3.3.10 has significantly reduced the number of crashes for me.
Is it being solved?
Noticed its causing performance issues on flutter 3.7 as well
Here I have a comparison of a list containing images without memCacheWidth
and memCacheHeight
on release mode, vs with memCacheWidth
and memCacheHeight
in profile mode, the performance difference is quite signficant
Apologies for the quality due to compression, but do observe the CPU and GPU charts
Without memCacheHeight
and memCacheWidth
on release mode
With memCacheHeight
and memCacheWidth
on profile mode
Still an issue
Any updates?
Call evict on imageProvider get from CachedNetworkImage help me solve issue.
await imageProvider.obtainCacheStatus(configuration: ImageConfiguration.empty).then( (value) { imageProvider.evict(); }, );
Call evict on imageProvider get from CachedNetworkImage help me solve issue.
await imageProvider.obtainCacheStatus(configuration: ImageConfiguration.empty).then( (value) { imageProvider.evict(); }, );
@vuhoangminh91 Where do we need to add this line?
Any updates?
any updates?
any updates?
Hi, Allez gezellig! Thanks for creating this plugin. I was really concerned about my app crashes and then I did memory profiling on the app. And I am attaching the screenshots of heap dumps here. I have used Cached Network Image in my entire project and have not used Network. Image or any other plugin. As you can see that the images/ Cached Network Image is responsible for app crashes and out of memory errors. I have also read the similar issues here in issues section for the plugin. And now I am reading even Network.Image or ImagePicker causes the same kind of behavior if used without cacheWidth/width and cacheHeight. I think flutter team should update their official docs for these plugins/libraries so that unexpected behavior is not triggered causing the production app to crash if developers used these plugins without any recommendations to reduce memory consumption in the heap. It's also important to mention in Readme for this plugin to use all recommended measures such as memWidth, memHeight or use the 2.3.0 beta version so that future developers can use this plugin effectively to add multiple images in the app. It would be also helpful if you can elaborate on all these recommended measures here or update the Readme so that it can help the future developers. I also tried using 2.3.0-beta.1 in android app because that't the latest version on stable flutter channel. I also tried adding memWidth and memHeight parameters to CachedNetworkImage. But to no avail. That's why need some recommendations from the contributors. I saw the code for CachedNetwork image too and it's using default cache manager from flutter cache manager package if not provided with cache manager as parameter. The only thing that now I could try is to limit the maximum number of cache objects for Cache Manager which is passed as parameter to the Cached Network Image. So my question to the team are as follows:
Looking forward to the reply. Dank je wel!