痛点场景:直接加载高清大图
假设你的应用需要显示用户相册:
NetworkImage('https://example.com/high-res-photo.jpg')
面临的问题:
- 📶 网络差时长时间白屏
- 💾 重复下载相同图片浪费流量
- 🔒 敏感图片无权限验证
- 📱 内存占用过高导致崩溃
代理模式解决方案
核心思想: 为其他对象提供一种代理以控制对这个对象的访问。
三种常见代理类型:
- 虚拟代理: 延迟加载大资源(如占位图→高清图)
- 保护代理: 控制访问权限
- 缓存代理: 存储请求结果避免重复计算/下载
Flutter图片加载代理实现
1. 定义图片加载接口
abstract class ImageLoader {
Future<ImageProvider> load(String url);
}
2. 实现真实图片加载器
class RealImageLoader implements ImageLoader {
Future<ImageProvider> load(String url) async {
print('真实加载器:从网络加载高清图片');
return NetworkImage(url);
}
}
3. 创建智能代理
class SmartImageLoader implements ImageLoader {
final RealImageLoader _realLoader = RealImageLoader();
final Map<String, ImageProvider> _cache = {};
Future<ImageProvider> load(String url) async {
// 1. 检查缓存
if (_cache.containsKey(url)) {
print('代理:从缓存返回图片');
return _cache[url]!;
}
// 2. 显示占位图
print('代理:先返回低分辨率占位图');
final placeholder = ResizeImage(
NetworkImage('$url?thumb=true'),
width: 100,
height: 100,
);
// 3. 后台加载高清图
_realLoader.load(url).then((highResImage) {
print('代理:高清图加载完成,更新缓存');
_cache[url] = highResImage;
// 通知UI更新(可通过ChangeNotifier或Stream)
});
return placeholder;
}
}
4. 在Flutter中使用
class PhotoViewer extends StatelessWidget {
final ImageLoader loader = SmartImageLoader(); // 使用代理
Widget build(BuildContext context) {
return FutureBuilder<ImageProvider>(
future: loader.load('https://example.com/photo1.jpg'),
builder: (ctx, snapshot) {
if (snapshot.hasData) {
return Image(image: snapshot.data!);
}
return CircularProgressIndicator();
},
);
}
}
Flutter中的实际应用场景
场景1:权限控制代理
class AuthImageLoader implements ImageLoader {
final UserService _userService;
final RealImageLoader _realLoader;
Future<ImageProvider> load(String url) async {
if (!await _userService.checkPhotoPermission(url)) {
return AssetImage('assets/locked.png');
}
return _realLoader.load(url);
}
}
// 使用
ImageLoader loader = AuthImageLoader(userService, realLoader);
场景2:API请求缓存代理
class CachedApiClient implements ApiClient {
final ApiClient _realClient;
final Map<String, dynamic> _cache = {};
Future<dynamic> fetchData(String endpoint) async {
if (_cache.containsKey(endpoint)) {
return _cache[endpoint];
}
final data = await _realClient.fetchData(endpoint);
_cache[endpoint] = data;
return data;
}
}
// 使用
final apiClient = CachedApiClient(RealApiClient());
final product = await apiClient.fetchData('/products/123');
场景3:敏感操作延迟代理
class ExpenseReportGeneratorProxy implements ReportGenerator {
RealReportGenerator? _realGenerator;
Future<Report> generate() async {
if (!await _checkPermission()) throw UnauthorizedException();
_realGenerator ??= RealReportGenerator(); // 按需创建
return _realGenerator!.generate();
}
}
代理模式与Flutter状态管理结合
将图片代理与Provider结合:
class ImageLoaderProvider extends ChangeNotifier {
final ImageLoader _loader;
ImageProvider? _currentImage;
ImageLoaderProvider(this._loader);
Future<void> loadImage(String url) async {
_currentImage = await _loader.load(url);
notifyListeners();
}
}
// 使用
context.read<ImageLoaderProvider>().loadImage('photo.jpg');
Consumer<ImageLoaderProvider>(
builder: (context, provider, child) {
return provider.currentImage != null
? Image(image: provider.currentImage!)
: Placeholder();
}
)
代理模式最佳实践
何时使用代理模式:
- 需要延迟加载大资源时(虚拟代理)
- 需要控制原始对象访问权限时(保护代理)
- 需要缓存请求结果时(缓存代理)
- 需要记录日志或监控对象访问时
Flutter特化技巧:
// 组合多种代理 ImageLoader loader = LoggingImageLoader( CacheImageLoader( AuthImageLoader( RealImageLoader() ) ) ); // 使用Futures组合 Future<ImageProvider> load(String url) async { final lowRes = await _loadLowRes(url); final highRes = _loadHighRes(url); // 不await return ProxyImage(lowRes, highRes); }
性能优化:
// 预加载代理 class PrefetchImageLoader implements ImageLoader { final List<String> _prefetchUrls; void prefetchAll() { for (final url in _prefetchUrls) { _realLoader.load(url); // 不await,后台加载 } } }
测试策略:
test('缓存代理测试', () async { final mockLoader = MockImageLoader(); final proxy = CachedImageLoader(mockLoader); when(mockLoader.load('test.jpg')).thenAnswer((_) async => FakeImage()); // 第一次调用真实对象 await proxy.load('test.jpg'); // 第二次应从缓存返回 await proxy.load('test.jpg'); verify(mockLoader.load('test.jpg')).called(1); // 仅调用一次 });
代理模式 vs 装饰器模式
特性 | 代理模式 | 装饰器模式 |
---|---|---|
目的 | 控制访问 | 增强功能 |
关系 | 代理与真实对象关系固定 | 装饰器可递归嵌套 |
创建时机 | 通常提前知道真实对象 | 运行时动态组合 |
典型应用 | 懒加载、权限控制、缓存 | 流式处理、动态扩展 |
代理模式的高级变体
1. 远程代理(跨进程通信)
// 本地代理
class LocalImageProxy implements ImageLoader {
Future<ImageProvider> load(String url) async {
// 通过平台通道调用原生代码
final result = await MethodChannel('image_loader')
.invokeMethod('loadImage', {'url': url});
return MemoryImage(base64Decode(result));
}
}
2. 智能引用代理
class ImageRefProxy implements ImageLoader {
int _refCount = 0;
ImageProvider? _cachedImage;
Future<ImageProvider> load(String url) async {
_refCount++;
if (_cachedImage == null) {
_cachedImage = await _realLoader.load(url);
}
return _cachedImage!;
}
void release() {
_refCount--;
if (_refCount == 0) {
_cachedImage?.evict(); // 释放内存
_cachedImage = null;
}
}
}
3. 日志监控代理
class AnalyticsImageLoader implements ImageLoader {
final AnalyticsService _analytics;
final ImageLoader _realLoader;
Future<ImageProvider> load(String url) async {
_analytics.logEvent('image_load_start', {'url': url});
final stopwatch = Stopwatch()..start();
try {
final image = await _realLoader.load(url);
_analytics.logEvent('image_load_success', {
'url': url,
'duration': stopwatch.elapsedMilliseconds,
});
return image;
} catch (e) {
_analytics.logError('image_load_failed', {'url': url});
rethrow;
}
}
}
总结:代理模式是你的访问管家
- 核心价值: 在访问真实对象前后插入额外逻辑
- Flutter优势:
- 实现图片懒加载和缓存
- 控制敏感资源访问
- 减少网络请求和计算开销
- 添加监控和日志记录
- 适用场景: 图片加载、API调用、权限管理、性能优化
🕶️ 设计启示: 当你需要在访问对象时加入"中间层"控制时,代理模式就是你的"隐形眼镜",既能看清需求,又能保护眼睛!