From 95f2e981da282ac8ccd0deb184493be30754e944 Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Tue, 9 Aug 2016 11:25:43 -0700 Subject: [PATCH] Provide an observatory extension to evict resources (#5241) ...so that you can use hot reload mode to update assets. --- .../lib/src/services/asset_bundle.dart | 14 +++++++++++ .../flutter/lib/src/services/binding.dart | 18 +++++++++++++++ .../flutter/lib/src/services/image_cache.dart | 10 ++++++++ .../flutter/lib/src/widgets/framework.dart | 23 +++++++++++++++++++ packages/flutter/lib/src/widgets/image.dart | 6 +++++ 5 files changed, 71 insertions(+) diff --git a/packages/flutter/lib/src/services/asset_bundle.dart b/packages/flutter/lib/src/services/asset_bundle.dart index 0cdfff531f..31bd199161 100644 --- a/packages/flutter/lib/src/services/asset_bundle.dart +++ b/packages/flutter/lib/src/services/asset_bundle.dart @@ -62,6 +62,11 @@ abstract class AssetBundle { /// used with one parser for the lifetime of the asset bundle. Future loadStructuredData(String key, dynamic parser(String value)); + /// If this is a caching asset bundle, and the given key describes a cached + /// asset, then evict the asset from the cache so that the next time it is + /// loaded, the cache will be reread from the asset bundle. + void evict(String key) { } + @override String toString() => '$runtimeType@$hashCode()'; } @@ -101,6 +106,9 @@ class NetworkAssetBundle extends AssetBundle { return parser(await loadString(key)); } + // TODO(ianh): Once the underlying network logic learns about caching, we + // should implement evict(). + @override String toString() => '$runtimeType@$hashCode($_baseUrl)'; } @@ -155,6 +163,12 @@ abstract class CachingAssetBundle extends AssetBundle { }); return completer.future; } + + @override + void evict(String key) { + _stringCache.remove(key); + _structuredDataCache.remove(key); + } } /// An [AssetBundle] that loads resources from a Mojo service. diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart index c223ffc3b2..415f4133a8 100644 --- a/packages/flutter/lib/src/services/binding.dart +++ b/packages/flutter/lib/src/services/binding.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'asset_bundle.dart'; +import 'image_cache.dart'; import 'shell.dart'; /// Ensures that the [MojoShell] singleton is created synchronously @@ -47,4 +48,21 @@ abstract class ServicesBinding extends BindingBase { } } } + + @override + void initServiceExtensions() { + super.initServiceExtensions(); + registerStringServiceExtension( + // ext.flutter.evict value=foo.png will cause foo.png to be evicted from the rootBundle cache + // and cause the entire image cache to be cleared. This is used by hot reload mode to clear + // out the cache of resources that have changed. + // TODO(ianh): find a way to only evict affected images, not all images + name: 'evict', + getter: () => '', + setter: (String value) { + rootBundle.evict(value); + imageCache.clear(); + } + ); + } } diff --git a/packages/flutter/lib/src/services/image_cache.dart b/packages/flutter/lib/src/services/image_cache.dart index ec517efd7a..c4b5ff670b 100644 --- a/packages/flutter/lib/src/services/image_cache.dart +++ b/packages/flutter/lib/src/services/image_cache.dart @@ -55,6 +55,16 @@ class ImageCache { } } + /// Evicts all entries from the cache. + /// + /// This is useful if, for instance, the root asset bundle has been updated + /// and therefore new images must be obtained. + // TODO(ianh): Provide a way to target individual images. This is currently non-trivial + // because by the time we get to the imageCache, the keys we're using are opaque. + void clear() { + _cache.clear(); + } + /// Returns the previously cached [ImageStream] for the given key, if available; /// if not, calls the given callback to obtain it first. In either case, the /// key is moved to the "most recently used" position. diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index a0cb221bb6..9045fc7032 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -731,6 +731,28 @@ abstract class State { @protected void didUpdateConfig(T oldConfig) { } + /// Called whenever the application is reassembled during debugging. + /// + /// This method should rerun any initialization logic that depends on global + /// state, for example, image loading from asset bundles (since the asset + /// bundle may have changed). + /// + /// In addition to this method being invoked, it is guaranteed that the + /// [build] method will be invoked when a reassemble is signalled. Most + /// widgets therefore do not need to do anything in the [reassemble] method. + /// + /// This function will only be called during development. In release builds, + /// the `ext.flutter.reassemble` hook is not available, and so this code will + /// never execute. + /// + /// See also: + /// + /// * [BindingBase.reassembleApplication] + /// * [Image], which uses this to reload images + @protected + @mustCallSuper + void reassemble() { } + /// Notify the framework that the internal state of this object has changed. /// /// Whenever you change the internal state of a [State] object, make the @@ -2061,6 +2083,7 @@ class StatefulElement extends ComponentElement { @override void _reassemble() { _builder = state.build; + state.reassemble(); super._reassemble(); } diff --git a/packages/flutter/lib/src/widgets/image.dart b/packages/flutter/lib/src/widgets/image.dart index d83c1501a9..7ee99fbc23 100644 --- a/packages/flutter/lib/src/widgets/image.dart +++ b/packages/flutter/lib/src/widgets/image.dart @@ -205,6 +205,12 @@ class _ImageState extends State { super.dependenciesChanged(); } + @override + void reassemble() { + _resolveImage(); + super.reassemble(); + } + @override void dispose() { _imageStream.removeListener(_handleImageChanged);