123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- import 'dart:async';
- import 'dart:convert';
- import 'dart:io';
- import 'dart:typed_data';
- import 'package:fis_common/helpers/http.dart';
- import 'package:fis_common/index.dart';
- import 'package:fis_common/logger/logger.dart';
- import 'package:flutter/foundation.dart';
- import 'package:flutter/services.dart';
- import 'package:path_provider/path_provider.dart';
- import 'consts.dart';
- import 'manifest.dart';
- import 'theme.dart';
- /// 资源来源
- enum FResourceFromType {
- local, // 本地
- network, // 网络
- }
- /// 资源配置信息实体
- class FResourceInfo {
- final String key;
- final FResourceFromType from;
- final String path;
- const FResourceInfo({
- required this.key,
- required this.from,
- required this.path,
- });
- factory FResourceInfo.fromJson(Map<String, dynamic> json) {
- return FResourceInfo(
- key: json['key'],
- from: FResourceFromType.values[json['from']],
- path: json['path'],
- );
- }
- }
- /// 资源索引键
- class FResourceKey {
- final String value;
- const FResourceKey(this.value);
- }
- /// Fis资源集
- abstract class FResourceBundle {
- static AssetBundle? assetBundle;
- late String _resourcePath;
- /// 资源路径
- String get resourcePath => _resourcePath;
- /// 设置资源路径
- void setResourcePath(String value) => _resourcePath = value;
- final Map<String, Future<String>> _stringCache = <String, Future<String>>{};
- final Map<String, Future<dynamic>> _structuredDataCache =
- <String, Future<dynamic>>{};
- Map<String, FResourceInfo> _resourceMap = Map<String, FResourceInfo>();
- /// 主题清单内存映射
- late FThemeManifestInfo _manifestInfoRefer;
- @protected
- Future<ByteData> _innerLoad(FResourceInfo info);
- /// 加载资源数据
- Future<ByteData> load(FResourceKey key) async {
- FResourceInfo? resourceInfo = _getInfo(key.value);
- if (resourceInfo == null) return ByteData(0);
- if (resourceInfo.from == FResourceFromType.network) {
- return await _loadFromNetwork(resourceInfo);
- }
- ByteData data;
- if (FTheme.ins.defaultName == _manifestInfoRefer.name) {
- var path =
- "packages/${FisUIConsts.themePackageName}/assets/${resourceInfo.path}";
- data = await (FResourceBundle.assetBundle ?? rootBundle).load(path);
- } else {
- data = await _innerLoad(resourceInfo);
- }
- return data;
- }
- Future<ByteData> _loadFromNetwork(FResourceInfo resourceInfo) async {
- try {
- var bytes = await FHttpHelper.downloadBytes(resourceInfo.path);
- if (bytes != null) {
- return bytes.buffer.asByteData();
- }
- } catch (e) {
- logger.e("Load resource-${resourceInfo.key} from network error.", e);
- }
- return ByteData(0);
- }
- /// 获取字符串
- Future<String> loadString(FResourceKey key, {bool cache = true}) {
- if (cache)
- return _stringCache.putIfAbsent(key.value, () => _innerLoadString(key));
- return _innerLoadString(key);
- }
- /// 获取结构化数据
- Future<T> loadStructuredData<T>(
- FResourceKey key, Future<T> parser(String value)) {
- String keyString = key.value;
- if (_structuredDataCache.containsKey(keyString))
- return _structuredDataCache[keyString]! as Future<T>;
- Completer<T>? completer;
- Future<T>? result;
- loadString(key, cache: false).then<T>(parser).then<void>((T value) {
- result = SynchronousFuture<T>(value);
- _structuredDataCache[keyString] = result!;
- if (completer != null) {
- completer.complete(value);
- }
- });
- if (result != null) {
- return result!;
- }
- completer = Completer<T>();
- _structuredDataCache[keyString] = completer.future;
- return completer.future;
- }
- /// 获取字节数组
- Future<Uint8List> loadAsBytes(FResourceKey key) async {
- var byteData = await load(key);
- return byteData.buffer.asUint8List();
- }
- /// 载入资源map和初始化
- void init(FThemeManifestInfo manifestInfo) {
- try {
- _manifestInfoRefer = manifestInfo;
- _resourceMap = Map.fromIterable(manifestInfo.resources,
- key: (item) => (item as FResourceInfo).key, value: (item) => item);
- clearCache();
- } catch (e) {}
- }
- /// 收回指定缓存
- void evict(FResourceKey key) {
- _stringCache.remove(key.value);
- _structuredDataCache.remove(key.value);
- }
- /// 清除缓存
- void clearCache() {
- _stringCache.clear();
- _structuredDataCache.clear();
- print('FResourceBundle clear cache!');
- }
- Future<String> _innerLoadString(FResourceKey key, {bool cache = true}) async {
- final ByteData data = await load(key);
- if (data.lengthInBytes < 50 * 1024) {
- return utf8.decode(data.buffer.asUint8List());
- }
- return compute(_utf8decode, data, debugLabel: 'UTF8 decode for "$key"');
- }
- static String _utf8decode(ByteData data) {
- return utf8.decode(data.buffer.asUint8List());
- }
- @override
- String toString() => '${describeIdentity(this)}()';
- FResourceInfo? _getInfo(String key) {
- if (_resourceMap.containsKey(key)) {
- return _resourceMap[key];
- }
- return null;
- }
- }
- class _FResourceBundleNative extends FResourceBundle {
- Directory? _storageDirectory;
- @override
- Future<ByteData> _innerLoad(FResourceInfo info) async {
- try {
- final String storagePath = await _getStoragePath();
- final String themeName = super._manifestInfoRefer.name;
- final String filePath = "$storagePath/themes/$themeName/${info.path}";
- final Uint8List bytes = await File(filePath).readAsBytes();
- return bytes.buffer.asByteData();
- } catch (e) {
- logger.e("Load resource-${info.key} from file error.", e);
- }
- return ByteData(0);
- }
- Future<String> _getStoragePath() async {
- if (_storageDirectory == null) {
- _storageDirectory = await getApplicationDocumentsDirectory();
- }
- return _storageDirectory!.path;
- }
- }
- class _FResourceBundleWeb extends FResourceBundle {
- @override
- Future<ByteData> _innerLoad(FResourceInfo info) async {
- String url =
- "${_getUrlRoot()}/${super._manifestInfoRefer.name}/${info.path}";
- Uint8List? data = await FHttpHelper.downloadBytes(url);
- if (data != null) {
- return data.buffer.asByteData();
- }
- return ByteData(0);
- }
- @protected
- String _getUrlRoot() => resourcePath;
- }
- class _FResourceBundleShellWeb extends _FResourceBundleWeb {
- @override
- String _getUrlRoot() => "http://resource.fis.plus/themes";
- }
- FResourceBundle _createBundle() {
- switch (FPlatform.current) {
- case FPlatformEnum.android:
- case FPlatformEnum.iOS:
- return _FResourceBundleNative();
- case FPlatformEnum.web:
- return _FResourceBundleWeb();
- case FPlatformEnum.webOnWin:
- case FPlatformEnum.webOnMac:
- return _FResourceBundleShellWeb();
- }
- }
- final FResourceBundle fRootBundle = _createBundle();
|