-
-
Notifications
You must be signed in to change notification settings - Fork 639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PR for review only - Feature/generic cache manager #6
Conversation
lib/cached_network_image.dart
Outdated
@@ -71,6 +71,10 @@ class _CachedNetworkImageState extends State<CachedNetworkImage> { | |||
} | |||
|
|||
class CacheManager { | |||
static Duration inbetweenCleans = new Duration(days: 7); | |||
static Duration maxAgeCacheObject = new Duration(days: 30); | |||
static int maxNrOfCacheObjects = 200; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would developers be able to change these config parameters . The inbetweenCleans
for example might be too big?
Same with maxNrOfCacheObjects
. if objects are small I might opt in to store more..?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are public statics, so they can be set when you start the app.
lib/cached_network_image.dart
Outdated
|
||
//get saved cache data from shared prefs | ||
var jsonCacheString = prefs.getString("lib_cached_image_data"); | ||
cacheData = new Map(); | ||
var jsonCacheString = _prefs.getString("lib_cached_image_data"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should CacheManage know that it caches images? lib_cached_image_data
implies it knows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The key is lib_cached_image_data just because I called it that when I made this library. If we change it we should also add some migration and I didn't think that was worth it.
lib/cached_network_image.dart
Outdated
}); | ||
} | ||
|
||
// Get data about when the last clean action has been performed | ||
var cleanMillis = _prefs.getInt("lib_cached_image_data_last_clean"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of putting comment you could extract method with the same name
lastCacheClean = _getLastCleanTimestampFromPreferences();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I probably have to refactor a bit. I also want to extract the classes in multiple files.
lib/cached_network_image.dart
Outdated
_prefs.setString("lib_cached_image_data", JSON.encode(json)); | ||
|
||
if(await _shouldSaveAgain()){ | ||
await _saveDataInPrefs(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like _saveDataInPrefs
calls _saveDataInPrefs
again and uses number of special flags to control that behavior and avoid infinite loop.
what is the reason we need to do Save
and then again Save
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic I use here is the following.
Imagine you download 5 files simultaneously. You would end up in 'Save' 5 times. If the first one is still busy saving the other 4 just indicate that something has changed and return. The first one will save one more time in the end, resulting in 2 saves instead of 5. It also only cleans the cache once.
lib/cached_network_image.dart
Outdated
await synchronized(_lock, () async { | ||
var oldestDateAllowed = new DateTime.now().subtract(maxAgeCacheObject); | ||
|
||
//Remove old objects |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can be function _removeTooOldToKeepObjects
(i.e. comment will not be necessary)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I'll refactor it a bit.
lib/cached_network_image.dart
Outdated
if (_cacheData.length > maxNrOfCacheObjects) { | ||
var allValues = _cacheData.values.toList(); | ||
allValues.sort((c1, c2) => c1.touched.compareTo(c2.touched)); | ||
for (var i = allValues.length; i > maxNrOfCacheObjects; i--) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could simplify and use take(count)
to get oldest objects. something like that:
allValues.sort((c1, c2) => c2.touched.compareTo(c1.touched)); // sort OLDEST first
allValues.take( _cacheData.length - maxNrOfCacheObjects); // get them
allValues.forEach( (item) {await _removeFile(lastItem)} ) //remove them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, I am still learning dart so didn't think about this.
lib/cached_network_image.dart
Outdated
if (response != null) { | ||
if (response.statusCode == 200) { | ||
await newCache.setDataFromHeaders(response.headers); | ||
var folder = new File(newCache.filePath).parent; | ||
var folder = new File(newCache.filePath).parent; | ||
if (!(await folder.exists())) { | ||
folder.createSync(recursive: true); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like sole folders are being created. I am not sure if we delete any of these folders..
what is the reason to have these folders? I thought that files will have just GUIDS as names so could stored flat?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes they are practically stored flat. It only creates one folder so all cache files are stored in a folder and cannot conflict with random other files in de cache folder.
lib/cached_network_image.dart
Outdated
setDataFromHeaders(Map<String, String> headers) async { | ||
//Without a cache-control header we keep the file for a week | ||
var ageDuration = new Duration(days: 7); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting. that contradicts earlier Duraton of 30 days (static Duration maxAgeCacheObject = new Duration(days: 30);
)
I think this is still CacheManager configuration - it belongs to CacheManager
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't really now how to name this variable, but the functionality is different.
The CacheManager configuration is about when to delete a file and checks when the file is last used by the app.
This 'ageDuration' indicates for how long the file is valid and should normally be set by the cache-control. When the user wants the file but it is not valid anymore it has to pull it from the server again whether the file is still in the cache or not. I didn't want to make it infinite as that might result in problems when a file is changed without the url changing.
lib/cached_network_image.dart
Outdated
touched = new DateTime.now(); | ||
_map["touched"] = touched.millisecondsSinceEpoch; | ||
} | ||
|
||
setDataFromHeaders(Map<String, String> headers) async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setDataFromHeaders
is very coupled with HttpHeaders. Cache Objects is not support to know about headers - it knows the object, knows when it was touched etc...
parsing HttpHeaders sounds like an infrastructure concern? Currently Cache Manager knows that files are being downloaded from Http so it shall parse HttpHeaders (or delegate it to some parser etc..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is probably a good idea.
lib/cached_network_image.dart
Outdated
Directory directory = await getTemporaryDirectory(); | ||
var folder = new Directory("${directory.path}/cache"); | ||
if (!(await folder.exists())) { | ||
folder.createSync(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another place where we create folders ...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same folder as earlier. It is probably better to only do it in one place. I did this because otherwise it broke when the file was gone but the filepath was already determined in the cache manager.
lib/cached_network_image.dart
Outdated
//If we have never downloaded this file, do download | ||
if (cacheObject.filePath == null) { | ||
cacheData[url] = await downloadFile(url); | ||
_cacheData[url] = await downloadFile(url); | ||
return; | ||
} | ||
//If file is removed from the cache storage, download again |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure why If we have never downloaded this file, do download
has to be different to If file is removed from the cache storage, download again
.
It can be exactly same flow - High level logic:
if(!exsitsInCache){
fetchFromNetwork();
}
return fromCache;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, but you would get new File(null).exists()
, is that allowed? Otherwise you still have to do some checks whether path == null and I don't directly see how this can be made cleaner.
lib/cached_network_image.dart
Outdated
@@ -170,11 +270,11 @@ class CacheManager { | |||
var response; | |||
try { | |||
response = await http.get(url, headers: headers); | |||
}catch(e){} | |||
} catch (e) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- what is the default timeout on
.get
- how we can let developers know that
get
failed with certain error. - how developers can configure/get this error and decide what to do (present another image?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't think about it yet. I do agree that it is a good idea improve the error handling.
lib/cached_network_image.dart
Outdated
return true; | ||
} | ||
_isStoringData = false; | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_shouldStoreDataAgain
and _isStoringData
two very puzzling fields...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my other comment about saving. It is meant that you don't save more than necessary.
What do you think about the serialization of this data? It was a quick way, but I am not sure if it is the best way. I do think that sqlite is a bit overkill though.
Moved cache manager to flutter_cache_manager Only support ImageProvider from now on
No description provided.