mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Merge pull request #2828 from Hixie/lru
Replace use of LruMap with a pure LinkedHashMap
This commit is contained in:
commit
da88f1d01f
@ -3,10 +3,10 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:ui' show hashValues;
|
||||
|
||||
import 'package:mojo/mojo/url_response.mojom.dart';
|
||||
import 'package:quiver/collection.dart';
|
||||
|
||||
import 'fetch.dart';
|
||||
import 'image_decoder.dart';
|
||||
@ -25,6 +25,12 @@ import 'image_resource.dart';
|
||||
/// share the same cache as all the other image loading codepaths that used the
|
||||
/// [imageCache].
|
||||
abstract class ImageProvider { // ignore: one_member_abstracts
|
||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||
/// const constructors so that they can be used in const expressions.
|
||||
const ImageProvider();
|
||||
|
||||
/// Subclasses must implement this method by having it asynchronously return
|
||||
/// an [ImageInfo] that represents the image provided by this [ImageProvider].
|
||||
Future<ImageInfo> loadImage();
|
||||
|
||||
/// Subclasses must implement the `==` operator so that the image cache can
|
||||
@ -88,22 +94,34 @@ const int _kDefaultSize = 1000;
|
||||
class ImageCache {
|
||||
ImageCache._();
|
||||
|
||||
final LruMap<ImageProvider, ImageResource> _cache =
|
||||
new LruMap<ImageProvider, ImageResource>(maximumSize: _kDefaultSize);
|
||||
final LinkedHashMap<ImageProvider, ImageResource> _cache =
|
||||
new LinkedHashMap<ImageProvider, ImageResource>();
|
||||
|
||||
/// Maximum number of entries to store in the cache.
|
||||
///
|
||||
/// Once this many entries have been cached, the least-recently-used entry is
|
||||
/// evicted when adding a new entry.
|
||||
int get maximumSize => _cache.maximumSize;
|
||||
int get maximumSize => _maximumSize;
|
||||
int _maximumSize = _kDefaultSize;
|
||||
/// Changes the maximum cache size.
|
||||
///
|
||||
/// If the new size is smaller than the current number of elements, the
|
||||
/// extraneous elements are evicted immediately. Setting this to zero and then
|
||||
/// returning it to its original value will therefore immediately clear the
|
||||
/// cache. However, doing this is not very efficient.
|
||||
// (the quiver library does it one at a time rather than using clear())
|
||||
void set maximumSize(int value) { _cache.maximumSize = value; }
|
||||
/// cache.
|
||||
void set maximumSize(int value) {
|
||||
assert(value != null);
|
||||
assert(value >= 0);
|
||||
if (value == maximumSize)
|
||||
return;
|
||||
_maximumSize = value;
|
||||
if (maximumSize == 0) {
|
||||
_cache.clear();
|
||||
} else {
|
||||
while (_cache.length > maximumSize)
|
||||
_cache.remove(_cache.keys.first);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the [ImageProvider.loadImage] method on the given image provider, if
|
||||
/// necessary, and returns an [ImageResource] that encapsulates a [Future] for
|
||||
@ -113,9 +131,20 @@ class ImageCache {
|
||||
/// cache, then the [ImageResource] object is immediately usable and the
|
||||
/// provider is not invoked.
|
||||
ImageResource loadProvider(ImageProvider provider) {
|
||||
return _cache.putIfAbsent(provider, () {
|
||||
return new ImageResource(provider.loadImage());
|
||||
});
|
||||
ImageResource result = _cache[provider];
|
||||
if (result != null) {
|
||||
_cache.remove(provider);
|
||||
} else {
|
||||
if (_cache.length == maximumSize && maximumSize > 0)
|
||||
_cache.remove(_cache.keys.first);
|
||||
result = new ImageResource(provider.loadImage());;
|
||||
}
|
||||
if (maximumSize > 0) {
|
||||
assert(_cache.length < maximumSize);
|
||||
_cache[provider] = result;
|
||||
}
|
||||
assert(_cache.length <= maximumSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Fetches the given URL, associating it with the given scale.
|
||||
|
||||
@ -8,7 +8,6 @@ dependencies:
|
||||
collection: '>=1.4.0 <2.0.0'
|
||||
intl: '>=0.12.4+2 <0.13.0'
|
||||
vector_math: '>=1.4.5 <2.0.0'
|
||||
quiver: '>=0.21.4 <0.22.0'
|
||||
|
||||
sky_engine:
|
||||
path: ../../bin/cache/pkg/sky_engine
|
||||
|
||||
41
packages/flutter/test/services/image_cache_resize_test.dart
Normal file
41
packages/flutter/test/services/image_cache_resize_test.dart
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'mocks_for_image_cache.dart';
|
||||
|
||||
void main() {
|
||||
test('Image cache resizing', () async {
|
||||
|
||||
imageCache.maximumSize = 2;
|
||||
|
||||
TestImageInfo a = (await imageCache.loadProvider(new TestProvider(1, 1)).first);
|
||||
TestImageInfo b = (await imageCache.loadProvider(new TestProvider(2, 2)).first);
|
||||
TestImageInfo c = (await imageCache.loadProvider(new TestProvider(3, 3)).first);
|
||||
TestImageInfo d = (await imageCache.loadProvider(new TestProvider(1, 4)).first);
|
||||
expect(a.value, equals(1));
|
||||
expect(b.value, equals(2));
|
||||
expect(c.value, equals(3));
|
||||
expect(d.value, equals(4));
|
||||
|
||||
imageCache.maximumSize = 0;
|
||||
|
||||
TestImageInfo e = (await imageCache.loadProvider(new TestProvider(1, 5)).first);
|
||||
expect(e.value, equals(5));
|
||||
|
||||
TestImageInfo f = (await imageCache.loadProvider(new TestProvider(1, 6)).first);
|
||||
expect(f.value, equals(6));
|
||||
|
||||
imageCache.maximumSize = 3;
|
||||
|
||||
TestImageInfo g = (await imageCache.loadProvider(new TestProvider(1, 7)).first);
|
||||
expect(g.value, equals(7));
|
||||
|
||||
TestImageInfo h = (await imageCache.loadProvider(new TestProvider(1, 8)).first);
|
||||
expect(h.value, equals(7));
|
||||
|
||||
});
|
||||
}
|
||||
83
packages/flutter/test/services/image_cache_test.dart
Normal file
83
packages/flutter/test/services/image_cache_test.dart
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'mocks_for_image_cache.dart';
|
||||
|
||||
void main() {
|
||||
test('Image cache', () async {
|
||||
|
||||
imageCache.maximumSize = 3;
|
||||
|
||||
TestImageInfo a = (await imageCache.loadProvider(new TestProvider(1, 1)).first);
|
||||
expect(a.value, equals(1));
|
||||
TestImageInfo b = (await imageCache.loadProvider(new TestProvider(1, 2)).first);
|
||||
expect(b.value, equals(1));
|
||||
TestImageInfo c = (await imageCache.loadProvider(new TestProvider(1, 3)).first);
|
||||
expect(c.value, equals(1));
|
||||
TestImageInfo d = (await imageCache.loadProvider(new TestProvider(1, 4)).first);
|
||||
expect(d.value, equals(1));
|
||||
TestImageInfo e = (await imageCache.loadProvider(new TestProvider(1, 5)).first);
|
||||
expect(e.value, equals(1));
|
||||
TestImageInfo f = (await imageCache.loadProvider(new TestProvider(1, 6)).first);
|
||||
expect(f.value, equals(1));
|
||||
|
||||
expect(f, equals(a));
|
||||
|
||||
// cache still only has one entry in it: 1(1)
|
||||
|
||||
TestImageInfo g = (await imageCache.loadProvider(new TestProvider(2, 7)).first);
|
||||
expect(g.value, equals(7));
|
||||
|
||||
// cache has two entries in it: 1(1), 2(7)
|
||||
|
||||
TestImageInfo h = (await imageCache.loadProvider(new TestProvider(1, 8)).first);
|
||||
expect(h.value, equals(1));
|
||||
|
||||
// cache still has two entries in it: 2(7), 1(1)
|
||||
|
||||
TestImageInfo i = (await imageCache.loadProvider(new TestProvider(3, 9)).first);
|
||||
expect(i.value, equals(9));
|
||||
|
||||
// cache has three entries in it: 2(7), 1(1), 3(9)
|
||||
|
||||
TestImageInfo j = (await imageCache.loadProvider(new TestProvider(1, 10)).first);
|
||||
expect(j.value, equals(1));
|
||||
|
||||
// cache still has three entries in it: 2(7), 3(9), 1(1)
|
||||
|
||||
TestImageInfo k = (await imageCache.loadProvider(new TestProvider(4, 11)).first);
|
||||
expect(k.value, equals(11));
|
||||
|
||||
// cache has three entries: 3(9), 1(1), 4(11)
|
||||
|
||||
TestImageInfo l = (await imageCache.loadProvider(new TestProvider(1, 12)).first);
|
||||
expect(l.value, equals(1));
|
||||
|
||||
// cache has three entries: 3(9), 4(11), 1(1)
|
||||
|
||||
TestImageInfo m = (await imageCache.loadProvider(new TestProvider(2, 13)).first);
|
||||
expect(m.value, equals(13));
|
||||
|
||||
// cache has three entries: 4(11), 1(1), 2(13)
|
||||
|
||||
TestImageInfo n = (await imageCache.loadProvider(new TestProvider(3, 14)).first);
|
||||
expect(n.value, equals(14));
|
||||
|
||||
// cache has three entries: 1(1), 2(13), 3(14)
|
||||
|
||||
TestImageInfo o = (await imageCache.loadProvider(new TestProvider(4, 15)).first);
|
||||
expect(o.value, equals(15));
|
||||
|
||||
// cache has three entries: 2(13), 3(14), 4(15)
|
||||
|
||||
TestImageInfo p = (await imageCache.loadProvider(new TestProvider(1, 16)).first);
|
||||
expect(p.value, equals(16));
|
||||
|
||||
// cache has three entries: 3(14), 4(15), 1(16)
|
||||
|
||||
});
|
||||
}
|
||||
48
packages/flutter/test/services/mocks_for_image_cache.dart
Normal file
48
packages/flutter/test/services/mocks_for_image_cache.dart
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:ui' as ui show Image;
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class TestImageInfo implements ImageInfo {
|
||||
const TestImageInfo(this.value) : image = null, scale = null;
|
||||
|
||||
@override
|
||||
final ui.Image image; // ignored in test
|
||||
|
||||
@override
|
||||
final double scale; // ignored in test
|
||||
|
||||
final int value;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($value)';
|
||||
}
|
||||
|
||||
class TestProvider extends ImageProvider {
|
||||
const TestProvider(this.equalityValue, this.imageValue);
|
||||
final int imageValue;
|
||||
final int equalityValue;
|
||||
|
||||
@override
|
||||
Future<ImageInfo> loadImage() async {
|
||||
return new TestImageInfo(imageValue);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (other is! TestProvider)
|
||||
return false;
|
||||
final TestProvider typedOther = other;
|
||||
return equalityValue == typedOther.equalityValue;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => equalityValue.hashCode;
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType($equalityValue, $imageValue)';
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user