Detect sync vs async obtainKey in ResizeImage (#51081)

This commit is contained in:
Gary Qian 2020-02-19 19:06:03 -08:00 committed by GitHub
parent dda7a618ec
commit af17860513
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 3 deletions

View file

@ -726,9 +726,28 @@ class ResizeImage extends ImageProvider<_SizeAwareCacheKey> {
}
@override
Future<_SizeAwareCacheKey> obtainKey(ImageConfiguration configuration) async {
final Object providerCacheKey = await imageProvider.obtainKey(configuration);
return _SizeAwareCacheKey(providerCacheKey, width, height);
Future<_SizeAwareCacheKey> obtainKey(ImageConfiguration configuration) {
Completer<_SizeAwareCacheKey> completer;
// If the imageProvider.obtainKey future is synchronous, then we will be able to fill in result with
// a value before completer is initialized below.
SynchronousFuture<_SizeAwareCacheKey> result;
imageProvider.obtainKey(configuration).then((Object key) {
if (completer == null) {
// This future has completed synchronously (completer was never assigned),
// so we can directly create the synchronous result to return.
result = SynchronousFuture<_SizeAwareCacheKey>(_SizeAwareCacheKey(key, width, height));
} else {
// This future did not synchronously complete.
completer.complete(_SizeAwareCacheKey(key, width, height));
}
});
if (result != null) {
return result;
}
// If the code reaches here, it means the the imageProvider.obtainKey was not
// completed sync, so we initialize the completer for completion later.
completer = Completer<_SizeAwareCacheKey>();
return completer.future;
}
}

View file

@ -395,6 +395,32 @@ void main() {
resizeImage.load(await resizeImage.obtainKey(ImageConfiguration.empty), decode);
});
test('ResizeImage handles sync obtainKey', () async {
final Uint8List bytes = Uint8List.fromList(kTransparentImage);
final MemoryImage memoryImage = MemoryImage(bytes);
final ResizeImage resizeImage = ResizeImage(memoryImage, width: 123, height: 321);
bool isAsync = false;
resizeImage.obtainKey(ImageConfiguration.empty).then((Object key) {
expect(isAsync, false);
});
isAsync = true;
expect(isAsync, true);
});
test('ResizeImage handles async obtainKey', () async {
final Uint8List bytes = Uint8List.fromList(kTransparentImage);
final AsyncKeyMemoryImage memoryImage = AsyncKeyMemoryImage(bytes);
final ResizeImage resizeImage = ResizeImage(memoryImage, width: 123, height: 321);
bool isAsync = false;
resizeImage.obtainKey(ImageConfiguration.empty).then((Object key) {
expect(isAsync, true);
});
isAsync = true;
expect(isAsync, true);
});
test('File image with empty file throws expected error (load)', () async {
final Completer<StateError> error = Completer<StateError>();
FlutterError.onError = (FlutterErrorDetails details) {
@ -425,6 +451,17 @@ Future<Size> _resolveAndGetSize(ImageProvider imageProvider,
return await completer.future;
}
// This version of MemoryImage guarantees obtainKey returns a future that has not been
// completed synchronously.
class AsyncKeyMemoryImage extends MemoryImage {
AsyncKeyMemoryImage(Uint8List bytes) : super(bytes);
@override
Future<MemoryImage> obtainKey(ImageConfiguration configuration) {
return Future<MemoryImage>(() => this);
}
}
class MockHttpClient extends Mock implements HttpClient {}
class MockHttpClientRequest extends Mock implements HttpClientRequest {}
class MockHttpClientResponse extends Mock implements HttpClientResponse {}