This commit is contained in:
roxy 2026-06-09 14:30:48 +08:00
parent 8ac9590a8f
commit 142dcebaae
12 changed files with 266 additions and 350 deletions

View File

@ -524,7 +524,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 100;
CURRENT_PROJECT_VERSION = 161;
DEVELOPMENT_TEAM = S9YG87C297;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@ -533,7 +533,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 1.6.1;
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -715,7 +715,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 100;
CURRENT_PROJECT_VERSION = 161;
DEVELOPMENT_TEAM = S9YG87C297;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@ -724,7 +724,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 1.6.1;
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -744,7 +744,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 100;
CURRENT_PROJECT_VERSION = 161;
DEVELOPMENT_TEAM = S9YG87C297;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@ -753,7 +753,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 1.6.1;
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -7,7 +7,6 @@ class BaishunBridgeActions {
static const String gameLoaded = 'gameLoaded';
static const String gameRecharge = 'gameRecharge';
static const String destroy = 'destroy';
static const String debugLog = 'debugLog';
}
class BaishunBridgeMessage {
@ -104,45 +103,6 @@ class BaishunJsBridge {
$channelName.postMessage(JSON.stringify({ action: action, payload: payload }));
return payload;
}
function clipText(value, limit) {
const text = (value == null ? '' : String(value)).trim();
if (!text) {
return '';
}
return text.length > limit ? text.slice(0, limit) + '...' : text;
}
function stringifyForLog(value) {
if (value == null) {
return '';
}
if (typeof value === 'string') {
return clipText(value, 320);
}
try {
return clipText(JSON.stringify(value), 320);
} catch (_) {
return clipText(String(value), 320);
}
}
function postDebug(tag, payload) {
$channelName.postMessage(JSON.stringify({
action: '${BaishunBridgeActions.debugLog}',
payload: {
tag: tag,
message: stringifyForLog(payload)
}
}));
}
function maskText(value) {
const text = (value == null ? '' : String(value)).trim();
if (!text) {
return '';
}
if (text.length <= 10) {
return text;
}
return text.slice(0, 6) + '***' + text.slice(-4);
}
function ensureChannelAlias(name, handler) {
if (!window[name] || typeof window[name].postMessage !== 'function') {
window[name] = { postMessage: handler };
@ -156,30 +116,21 @@ class BaishunJsBridge {
}
}
window.NativeBridge.getConfigSync = function() {
postDebug('bridge.getConfigSync', {
hasConfig: !!window.__baishunLastConfig,
hasNativeBridgeConfig: !!(window.NativeBridge && window.NativeBridge.config)
});
return window.__baishunLastConfig || null;
};
window.NativeBridge.getConfig = function(params) {
postDebug('bridge.getConfig.call', toPayload(params));
postAction('${BaishunBridgeActions.getConfig}', params);
return window.__baishunLastConfig || null;
};
window.NativeBridge.destroy = function(payload) {
postDebug('bridge.destroy.call', toPayload(payload));
postAction('${BaishunBridgeActions.destroy}', payload);
};
window.NativeBridge.gameRecharge = function(payload) {
postDebug('bridge.gameRecharge.call', toPayload(payload));
postAction('${BaishunBridgeActions.gameRecharge}', payload);
};
window.NativeBridge.gameLoaded = function(payload) {
postDebug('bridge.gameLoaded.call', toPayload(payload));
postAction('${BaishunBridgeActions.gameLoaded}', payload);
};
window.__baishunDebugLog = postDebug;
ensureChannelAlias('getConfig', function(message) {
window.NativeBridge.getConfig(message);
});
@ -204,163 +155,6 @@ class BaishunJsBridge {
ensureWebkitHandler('gameLoaded', function(message) {
window.NativeBridge.gameLoaded(message);
});
if (!window.__baishunNetworkDebugReady) {
window.__baishunNetworkDebugReady = true;
if (typeof document !== 'undefined' && typeof document.addEventListener === 'function' && !window.__baishunInputDebugReady) {
window.__baishunInputDebugReady = true;
window.__baishunInputDebugCount = 0;
['pointerdown', 'touchstart', 'mousedown', 'click'].forEach(function(eventName) {
document.addEventListener(eventName, function(event) {
if ((window.__baishunInputDebugCount || 0) >= 24) {
return;
}
window.__baishunInputDebugCount = (window.__baishunInputDebugCount || 0) + 1;
const target = event && event.target;
const touch = event && event.touches && event.touches.length ? event.touches[0] : null;
postDebug('input.' + eventName, {
x: touch ? touch.clientX : (event && event.clientX),
y: touch ? touch.clientY : (event && event.clientY),
pointerType: event && event.pointerType,
targetTag: target && target.tagName,
targetId: target && target.id,
targetClass: target && target.className
});
}, true);
});
}
if (typeof window.addEventListener === 'function') {
window.addEventListener('error', function(event) {
const target = event && event.target;
if (target && target !== window) {
postDebug('resource.error', {
tag: target.tagName || '',
source: target.currentSrc || target.src || target.href || '',
outerHTML: target.outerHTML || ''
});
return;
}
postDebug('window.error', {
message: event && event.message,
source: event && event.filename,
line: event && event.lineno,
column: event && event.colno,
stack: event && event.error && event.error.stack
});
}, true);
window.addEventListener('unhandledrejection', function(event) {
postDebug('promise.reject', {
reason: event && event.reason
});
});
}
if (window.console && !window.__baishunConsoleDebugReady) {
window.__baishunConsoleDebugReady = true;
['log', 'info', 'warn', 'error'].forEach(function(level) {
const original = typeof window.console[level] === 'function'
? window.console[level].bind(window.console)
: null;
window.console[level] = function() {
try {
postDebug('console.' + level, Array.prototype.slice.call(arguments));
} catch (_) {}
if (original) {
return original.apply(window.console, arguments);
}
return undefined;
};
});
}
if (typeof window.fetch === 'function') {
const originalFetch = window.fetch.bind(window);
window.fetch = function(input, init) {
const url = typeof input === 'string' ? input : (input && input.url) || '';
const method = (init && init.method) || 'GET';
postDebug('fetch.start', { method: method, url: url });
return originalFetch(input, init)
.then(function(response) {
const cloned = response && typeof response.clone === 'function' ? response.clone() : null;
if (cloned && typeof cloned.text === 'function') {
cloned.text().then(function(text) {
if (!response.ok || /failed|error|exception|get user info/i.test(text)) {
postDebug('fetch.end', {
method: method,
url: url,
status: response.status,
body: text
});
} else {
postDebug('fetch.end', {
method: method,
url: url,
status: response.status
});
}
}).catch(function(error) {
postDebug('fetch.readError', {
method: method,
url: url,
error: error && error.message ? error.message : String(error)
});
});
} else {
postDebug('fetch.end', {
method: method,
url: url,
status: response && response.status
});
}
return response;
})
.catch(function(error) {
postDebug('fetch.error', {
method: method,
url: url,
error: error && error.message ? error.message : String(error)
});
throw error;
});
};
}
if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) {
const xhrOpen = window.XMLHttpRequest.prototype.open;
const xhrSend = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.open = function(method, url) {
this.__baishunMethod = method || 'GET';
this.__baishunUrl = url || '';
return xhrOpen.apply(this, arguments);
};
window.XMLHttpRequest.prototype.send = function(body) {
const method = this.__baishunMethod || 'GET';
const url = this.__baishunUrl || '';
postDebug('xhr.start', { method: method, url: url });
this.addEventListener('loadend', function() {
const responseText = typeof this.responseText === 'string' ? this.responseText : '';
if (this.status >= 400 || /failed|error|exception|get user info/i.test(responseText)) {
postDebug('xhr.end', {
method: method,
url: url,
status: this.status,
body: responseText
});
} else {
postDebug('xhr.end', {
method: method,
url: url,
status: this.status
});
}
});
this.addEventListener('error', function() {
postDebug('xhr.error', {
method: method,
url: url,
status: this.status
});
});
return xhrSend.apply(this, arguments);
};
}
}
window.baishunChannel = window.baishunChannel || {};
window.baishunChannel.walletUpdate = function(payload) {
window.__baishunLastWalletPayload = payload || {};
@ -377,10 +171,6 @@ class BaishunJsBridge {
if (typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') {
window.dispatchEvent(new CustomEvent('baishunBridgeReady'));
}
postDebug('bridge.ready', {
href: window.location && window.location.href,
ua: navigator && navigator.userAgent
});
})();
''';
}
@ -395,16 +185,6 @@ class BaishunJsBridge {
(function() {
const config = $payload;
const explicitCallbackPath = $encodedCallbackPath;
function maskText(value) {
const text = (value == null ? '' : String(value)).trim();
if (!text) {
return '';
}
if (text.length <= 10) {
return text;
}
return text.slice(0, 6) + '***' + text.slice(-4);
}
function resolvePath(path) {
if (!path || typeof path !== 'string') {
return null;
@ -444,20 +224,6 @@ class BaishunJsBridge {
if (explicitCallbackPath) {
window.__baishunLastJsCallback = explicitCallbackPath;
}
if (typeof window.__baishunDebugLog === 'function') {
window.__baishunDebugLog('config.deliver', {
appId: config.appId,
appChannel: config.appChannel,
userId: config.userId,
roomId: config.roomId,
gameMode: config.gameMode,
language: config.language,
gsp: config.gsp,
code: maskText(config.code),
codeLength: config.code ? String(config.code).length : 0,
callbackPath: callbackPath
});
}
if (typeof window.__baishunGetConfigCallback === 'function') {
window.__baishunGetConfigCallback(config);
window.__baishunGetConfigCallback = null;
@ -465,14 +231,6 @@ class BaishunJsBridge {
if (window.__baishunLastJsCallback) {
callbackInvoked = invokeCallbackPath(window.__baishunLastJsCallback) || callbackInvoked;
}
if (typeof window.__baishunDebugLog === 'function') {
window.__baishunDebugLog('config.callback', {
callbackPath: callbackPath,
callbackInvoked: callbackInvoked,
hasLegacyCallback: typeof window.__baishunGetConfigCallback === 'function',
hasOnBaishunConfig: typeof window.onBaishunConfig === 'function'
});
}
if (typeof window.onBaishunConfig === 'function') {
window.onBaishunConfig(config);
}
@ -494,9 +252,6 @@ class BaishunJsBridge {
return '''
(function() {
const payload = $safePayload;
if (typeof window.__baishunDebugLog === 'function') {
window.__baishunDebugLog('wallet.update', payload);
}
if (window.baishunChannel && typeof window.baishunChannel.walletUpdate === 'function') {
window.baishunChannel.walletUpdate(payload);
}

View File

@ -5,7 +5,6 @@ class LeaderBridgeActions {
static const String pay = 'pay';
static const String closeGame = 'closeGame';
static const String loadComplete = 'loadComplete';
static const String debugLog = 'debugLog';
}
class LeaderBridgeMessage {
@ -88,21 +87,6 @@ class LeaderJsBridge {
return {};
}
function stringifyForLog(value) {
if (value == null) {
return '';
}
if (typeof value === 'string') {
return value.length > 320 ? value.slice(0, 320) + '...' : value;
}
try {
const text = JSON.stringify(value);
return text.length > 320 ? text.slice(0, 320) + '...' : text;
} catch (_) {
return String(value);
}
}
function postMessage(action, input) {
const payload = toPayload(input);
$channelName.postMessage(JSON.stringify({
@ -112,13 +96,6 @@ class LeaderJsBridge {
return payload;
}
function postDebug(tag, payload) {
postMessage('${LeaderBridgeActions.debugLog}', {
tag: tag,
message: stringifyForLog(payload)
});
}
function invokeCallback(callback, payload) {
if (typeof callback === 'function') {
callback(payload);
@ -199,16 +176,6 @@ class LeaderJsBridge {
};
if (payloadChanged) {
dispatchConfigReady(normalizedPayload);
postDebug('config.ready', {
provider: normalizedPayload.provider || '',
gameId: normalizedPayload.gameId || '',
roomId: launchConfig.roomId || '',
code: launchConfig.code
? String(launchConfig.code).slice(0, 6) + '***'
: '',
codeLength: launchConfig.code ? String(launchConfig.code).length : 0,
entryUrl: entry.entryUrl || ''
});
}
}
@ -234,12 +201,6 @@ class LeaderJsBridge {
window.NativeBridge.pay = pay;
window.NativeBridge.closeGame = closeGame;
window.NativeBridge.loadComplete = loadComplete;
window.__leaderBridgeDebug = postDebug;
postDebug('bridge.ready', {
href: window.location && window.location.href,
hasUpdateCoin: typeof window.updateCoin === 'function'
});
}
applyLaunchPayload(launchPayload);
@ -262,13 +223,6 @@ class LeaderJsBridge {
window.onUpdateCoin();
return;
}
$channelName.postMessage(JSON.stringify({
action: '${LeaderBridgeActions.debugLog}',
payload: {
tag: 'game.updateCoin.missing',
message: 'No updateCoin handler found on game page.'
}
}));
})();
''';
}

View File

@ -299,12 +299,6 @@ class _BaishunGamePageState extends State<BaishunGamePage> {
Future<void> _dispatchBridgeMessage(
BaishunBridgeMessage bridgeMessage,
) async {
if (bridgeMessage.action == BaishunBridgeActions.debugLog) {
final tag = bridgeMessage.payload['tag']?.toString().trim();
final message = bridgeMessage.payload['message']?.toString() ?? '';
_log('h5_debug tag=${tag ?? 'unknown'} message=${_clip(message, 800)}');
return;
}
final callbackPath = _extractCallbackPath(bridgeMessage.payload);
_log(
'bridge_action action=${bridgeMessage.action} '

View File

@ -245,13 +245,6 @@ class _LeaderGamePageState extends State<LeaderGamePage> {
}
Future<void> _dispatchBridgeMessage(LeaderBridgeMessage bridgeMessage) async {
if (bridgeMessage.action == LeaderBridgeActions.debugLog) {
final tag = bridgeMessage.payload['tag']?.toString().trim();
final message = bridgeMessage.payload['message']?.toString() ?? '';
_log('h5_debug tag=${tag ?? 'unknown'} message=${_clip(message, 800)}');
return;
}
_didReceiveBridgeMessage = true;
_stopBridgeBootstrap(reason: 'bridge_message_${bridgeMessage.action}');
_log(

View File

@ -568,6 +568,7 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
late final SVGAAnimationController _controller;
bool _loaded = false;
bool _finished = false;
String? _retainedResource;
@override
void initState() {
@ -586,7 +587,10 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
Future<void> _loadAndPlay() async {
try {
final movieEntity = await _loadMovieEntity(widget.sourceUrl);
if (!mounted || _finished) return;
if (!mounted || _finished) {
_releaseRetainedResource();
return;
}
setState(() {
_loaded = true;
_controller.videoItem = movieEntity;
@ -600,9 +604,12 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
}
Future<MovieEntity> _loadMovieEntity(String sourceUrl) async {
final cache = SCGiftVapSvgaManager().videoItemCache;
final cached = cache[sourceUrl];
if (cached != null) return cached;
final manager = SCGiftVapSvgaManager();
final cached = manager.getCachedSvgaEntity(sourceUrl, retain: true);
if (cached != null) {
_replaceRetainedResource(sourceUrl);
return cached;
}
final pathType = SCPathUtils.getPathType(sourceUrl);
late final MovieEntity movieEntity;
@ -617,10 +624,35 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
throw Exception("Unsupported SVGA source: $sourceUrl");
}
movieEntity.autorelease = false;
cache[sourceUrl] = movieEntity;
if (!mounted || _finished) {
movieEntity.dispose();
return movieEntity;
}
manager.cacheSvgaEntity(sourceUrl, movieEntity, retain: true);
_replaceRetainedResource(sourceUrl);
return movieEntity;
}
void _replaceRetainedResource(String resource) {
final previous = _retainedResource;
if (previous == resource) {
return;
}
if (previous != null) {
SCGiftVapSvgaManager().releaseCachedSvgaEntity(previous);
}
_retainedResource = resource;
}
void _releaseRetainedResource() {
final resource = _retainedResource;
if (resource == null) {
return;
}
_retainedResource = null;
SCGiftVapSvgaManager().releaseCachedSvgaEntity(resource);
}
void _finish() {
if (_finished) return;
_finished = true;
@ -629,6 +661,7 @@ class _GiftWallSvgaEffectOverlayState extends State<_GiftWallSvgaEffectOverlay>
@override
void dispose() {
_releaseRetainedResource();
_controller.removeStatusListener(_handleStatusChanged);
_controller.dispose();
super.dispose();

View File

@ -91,11 +91,13 @@
// if (_isDisposed) return;
// if (SCPathUtils.getFileExtension(task.path).toLowerCase() == ".svga") {
// MovieEntity entity;
// if (SCGiftVapSvgaManager().videoItemCache.containsKey(task.path)) {
// entity = SCGiftVapSvgaManager().videoItemCache[task.path]!;
// final manager = SCGiftVapSvgaManager();
// final cached = manager.getCachedSvgaEntity(task.path);
// if (cached != null) {
// entity = cached;
// } else {
// entity = await SVGAParser.shared.decodeFromAssets(task.path);
// SCGiftVapSvgaManager().videoItemCache[task.path] = entity;
// manager.cacheSvgaEntity(task.path, entity);
// }
// _roomGiftsvgaController?.videoItem = entity;
// _roomGiftsvgaController?.reset();
@ -116,14 +118,16 @@
// if (_isDisposed) return;
// if (SCPathUtils.getFileExtension(task.path).toLowerCase() == ".svga") {
// MovieEntity? entity;
// if (SCGiftVapSvgaManager().videoItemCache.containsKey(task.path)) {
// entity = SCGiftVapSvgaManager().videoItemCache[task.path]!;
// final manager = SCGiftVapSvgaManager();
// final cached = manager.getCachedSvgaEntity(task.path);
// if (cached != null) {
// entity = cached;
// entity.autorelease = false;
// } else {
// try {
// entity = await SVGAParser.shared.decodeFromURL(task.path);
// entity.autorelease = false;
// SCGiftVapSvgaManager().videoItemCache[task.path] = entity;
// manager.cacheSvgaEntity(task.path, entity);
// } catch (e) {
// _isPlaying = false;
// }

View File

@ -26,12 +26,13 @@ class SCGiftVapSvgaManager {
static const int _maxConsecutiveEntryTasksBeforeGift = 2;
static const Duration _taskWatchdogTimeout = Duration(seconds: 20);
static const Duration _entryTaskTtl = Duration(seconds: 6);
Map<String, MovieEntity> videoItemCache = {};
final Map<String, MovieEntity> _videoItemCache = {};
static SCGiftVapSvgaManager? _inst;
static const int _maxPreloadConcurrency = 1;
static const int _maxPreloadQueueLength = 12;
static const int _maxSvgaCacheEntries = 8;
static const int _maxPlayablePathCacheEntries = 24;
final Map<String, int> _svgaRetainCounts = {};
SCGiftVapSvgaManager._internal();
@ -144,7 +145,7 @@ class SCGiftVapSvgaManager {
bool _isPreloadedOrLoading(String path) {
if (_needsSvgaController(path)) {
return videoItemCache.containsKey(path) ||
return _videoItemCache.containsKey(path) ||
_svgaLoadTasks.containsKey(path);
}
final pathType = SCPathUtils.getPathType(path);
@ -302,20 +303,80 @@ class SCGiftVapSvgaManager {
}
}
MovieEntity? getCachedSvgaEntity(String path, {bool retain = false}) {
final normalizedPath = path.trim();
if (normalizedPath.isEmpty) {
return null;
}
final entity = _touchCachedSvgaEntity(normalizedPath);
if (entity != null && retain) {
_retainCachedSvgaEntity(normalizedPath);
}
return entity;
}
void cacheSvgaEntity(String path, MovieEntity entity, {bool retain = false}) {
final normalizedPath = path.trim();
if (normalizedPath.isEmpty) {
_disposeSvgaEntityIfUnused(entity);
return;
}
_cacheSvgaEntity(normalizedPath, entity);
if (retain) {
_retainCachedSvgaEntity(normalizedPath);
}
}
MovieEntity? removeCachedSvgaEntity(String path, {bool dispose = false}) {
final normalizedPath = path.trim();
if (normalizedPath.isEmpty) {
return null;
}
final entity = _videoItemCache.remove(normalizedPath);
if (dispose &&
entity != null &&
!_isCachedSvgaEntityInUse(normalizedPath, entity)) {
_disposeSvgaEntityIfUnused(entity);
}
return entity;
}
void releaseCachedSvgaEntity(String path) {
final normalizedPath = path.trim();
if (normalizedPath.isEmpty) {
return;
}
final count = _svgaRetainCounts[normalizedPath] ?? 0;
if (count <= 1) {
_svgaRetainCounts.remove(normalizedPath);
_trimSvgaCache();
return;
}
_svgaRetainCounts[normalizedPath] = count - 1;
}
int get cachedSvgaEntityCount => _videoItemCache.length;
void _retainCachedSvgaEntity(String path) {
_svgaRetainCounts[path] = (_svgaRetainCounts[path] ?? 0) + 1;
}
MovieEntity? _touchCachedSvgaEntity(String path) {
final cached = videoItemCache.remove(path);
final cached = _videoItemCache.remove(path);
if (cached != null) {
videoItemCache[path] = cached;
_videoItemCache[path] = cached;
}
return cached;
}
void _cacheSvgaEntity(String path, MovieEntity entity) {
final previous = videoItemCache.remove(path);
if (previous != null && !identical(previous, entity)) {
final previous = _videoItemCache.remove(path);
if (previous != null &&
!identical(previous, entity) &&
!_isCachedSvgaEntityInUse(path, previous)) {
_disposeSvgaEntityIfUnused(previous);
}
videoItemCache[path] = entity;
_videoItemCache[path] = entity;
_trimSvgaCache();
}
@ -334,17 +395,19 @@ class SCGiftVapSvgaManager {
}
void _trimSvgaCache() {
while (videoItemCache.length > _maxSvgaCacheEntries) {
final removableEntry = videoItemCache.entries.firstWhere(
(entry) => !_isSvgaEntityInUse(entry.value),
orElse: () => videoItemCache.entries.first,
);
if (_isSvgaEntityInUse(removableEntry.value)) {
videoItemCache.remove(removableEntry.key);
videoItemCache[removableEntry.key] = removableEntry.value;
while (_videoItemCache.length > _maxSvgaCacheEntries) {
MapEntry<String, MovieEntity>? removableEntry;
for (final entry in _videoItemCache.entries) {
if (!_isCachedSvgaEntityInUse(entry.key, entry.value)) {
removableEntry = entry;
break;
}
}
if (removableEntry == null) {
break;
}
videoItemCache.remove(removableEntry.key);
_svgaRetainCounts.remove(removableEntry.key);
_videoItemCache.remove(removableEntry.key);
_disposeSvgaEntity(removableEntry.value);
}
}
@ -833,13 +896,15 @@ class SCGiftVapSvgaManager {
_preloadQueue.clear();
_queuedPreloadPaths.clear();
_activePreloadCount = 0;
final entries = videoItemCache.entries.toList(growable: false);
final entries = _videoItemCache.entries.toList(growable: false);
for (final entry in entries) {
final entity = entry.value;
if (!includeCurrent && _isSvgaEntityInUse(entity)) {
if (_isCachedSvgaEntityInUse(entry.key, entity) ||
(!includeCurrent && _isSvgaEntityInUse(entity))) {
continue;
}
videoItemCache.remove(entry.key);
_svgaRetainCounts.remove(entry.key);
_videoItemCache.remove(entry.key);
_disposeSvgaEntity(entity);
}
_playablePathCache.clear();
@ -849,6 +914,10 @@ class SCGiftVapSvgaManager {
return identical(_rsc?.videoItem, entity);
}
bool _isCachedSvgaEntityInUse(String path, MovieEntity entity) {
return _isSvgaEntityInUse(entity) || (_svgaRetainCounts[path] ?? 0) > 0;
}
void _disposeSvgaEntityIfUnused(MovieEntity entity) {
if (_isSvgaEntityInUse(entity)) {
return;

View File

@ -51,6 +51,7 @@ class _SVGAHeadwearWidgetState extends State<SVGAHeadwearWidget>
bool _isLoading = true;
bool _isNetworkResource = false;
bool _hasError = false;
String? _retainedResource;
@override
void initState() {
@ -84,8 +85,15 @@ class _SVGAHeadwearWidgetState extends State<SVGAHeadwearWidget>
try {
//
MovieEntity? videoItem;
final manager = SCGiftVapSvgaManager();
final shouldRetain = _retainedResource != widget.resource;
var retained = false;
if (widget.useCache) {
videoItem = SCGiftVapSvgaManager().videoItemCache[widget.resource];
videoItem = manager.getCachedSvgaEntity(
widget.resource,
retain: shouldRetain,
);
retained = videoItem != null && shouldRetain;
}
//
@ -97,11 +105,23 @@ class _SVGAHeadwearWidgetState extends State<SVGAHeadwearWidget>
videoItem.autorelease = false;
//
if (widget.useCache) {
SCGiftVapSvgaManager().videoItemCache[widget.resource] = videoItem;
if (!mounted) {
videoItem.dispose();
return;
}
manager.cacheSvgaEntity(
widget.resource,
videoItem,
retain: shouldRetain,
);
retained = shouldRetain;
}
}
if (mounted) {
if (retained) {
_replaceRetainedResource(widget.resource);
}
setState(() {
_animationController?.videoItem = videoItem;
_isLoading = false;
@ -164,13 +184,34 @@ class _SVGAHeadwearWidgetState extends State<SVGAHeadwearWidget>
void reload() {
if (widget.useCache) {
//
SCGiftVapSvgaManager().videoItemCache.remove(widget.resource);
SCGiftVapSvgaManager().removeCachedSvgaEntity(widget.resource);
}
_loadAnimation();
}
void _replaceRetainedResource(String resource) {
final previous = _retainedResource;
if (previous == resource) {
return;
}
if (previous != null) {
SCGiftVapSvgaManager().releaseCachedSvgaEntity(previous);
}
_retainedResource = resource;
}
void _releaseRetainedResource() {
final resource = _retainedResource;
if (resource == null) {
return;
}
_retainedResource = null;
SCGiftVapSvgaManager().releaseCachedSvgaEntity(resource);
}
@override
void dispose() {
_releaseRetainedResource();
stop();
_animationController?.dispose();
super.dispose();

View File

@ -270,6 +270,7 @@ class _RoomEntranceWidgetState extends State<RoomEntranceWidget>
bool _isLoading = false;
bool _hasError = false;
String? _currentResource;
String? _retainedResource;
// SVGAImage组件的key便
GlobalKey _svgaKey = GlobalKey();
@ -354,9 +355,13 @@ class _RoomEntranceWidgetState extends State<RoomEntranceWidget>
try {
final isNetworkResource = resource.startsWith('http');
MovieEntity? videoItem;
final manager = SCGiftVapSvgaManager();
final shouldRetain = _retainedResource != resource;
var retained = false;
if (widget.useCache) {
videoItem = SCGiftVapSvgaManager().videoItemCache[resource];
videoItem = manager.getCachedSvgaEntity(resource, retain: shouldRetain);
retained = videoItem != null && shouldRetain;
}
if (videoItem == null) {
@ -367,11 +372,19 @@ class _RoomEntranceWidgetState extends State<RoomEntranceWidget>
videoItem.autorelease = false;
if (widget.useCache) {
SCGiftVapSvgaManager().videoItemCache[resource] = videoItem;
if (!mounted) {
videoItem.dispose();
return;
}
manager.cacheSvgaEntity(resource, videoItem, retain: shouldRetain);
retained = shouldRetain;
}
}
if (mounted) {
if (retained) {
_replaceRetainedResource(resource);
}
//
await _applyDynamicData(videoItem, dynamicData);
@ -553,7 +566,7 @@ class _RoomEntranceWidgetState extends State<RoomEntranceWidget>
void reload() {
if (_currentResource != null) {
if (widget.useCache) {
SCGiftVapSvgaManager().videoItemCache.remove(_currentResource);
SCGiftVapSvgaManager().removeCachedSvgaEntity(_currentResource!);
}
_loadAnimation(
@ -566,6 +579,7 @@ class _RoomEntranceWidgetState extends State<RoomEntranceWidget>
@override
void dispose() {
_releaseRetainedResource();
stop();
_animationController?.dispose();
//
@ -573,6 +587,26 @@ class _RoomEntranceWidgetState extends State<RoomEntranceWidget>
super.dispose();
}
void _replaceRetainedResource(String resource) {
final previous = _retainedResource;
if (previous == resource) {
return;
}
if (previous != null) {
SCGiftVapSvgaManager().releaseCachedSvgaEntity(previous);
}
_retainedResource = resource;
}
void _releaseRetainedResource() {
final resource = _retainedResource;
if (resource == null) {
return;
}
_retainedResource = null;
SCGiftVapSvgaManager().releaseCachedSvgaEntity(resource);
}
@override
Widget build(BuildContext context) {
//

View File

@ -57,6 +57,7 @@ class _SCNetworkSvgaWidgetState extends State<SCNetworkSvgaWidget>
late final SVGAAnimationController _controller;
String? _loadedResource;
String? _retainedResource;
bool _hasError = false;
@override
@ -82,6 +83,7 @@ class _SCNetworkSvgaWidgetState extends State<SCNetworkSvgaWidget>
Future<void> _loadResource() async {
final resource = widget.resource.trim();
if (resource.isEmpty || !SCNetworkSvgaWidget.isSvga(resource)) {
_releaseRetainedResource();
setState(() {
_hasError = true;
_loadedResource = null;
@ -96,23 +98,39 @@ class _SCNetworkSvgaWidgetState extends State<SCNetworkSvgaWidget>
try {
_debugSvgaOnce(_loggedLoadingResources, 'loading', resource);
final cache = SCGiftVapSvgaManager().videoItemCache;
var movieEntity = cache[resource];
final manager = SCGiftVapSvgaManager();
final shouldRetain = _retainedResource != resource;
var movieEntity = manager.getCachedSvgaEntity(
resource,
retain: shouldRetain,
);
var retained = movieEntity != null && shouldRetain;
if (movieEntity == null) {
movieEntity =
resource.startsWith("http")
? await SVGAParser.shared.decodeFromURL(resource)
: await SVGAParser.shared.decodeFromAssets(resource);
movieEntity.autorelease = false;
cache[resource] = movieEntity;
if (!mounted || widget.resource.trim() != resource) {
movieEntity.dispose();
return;
}
manager.cacheSvgaEntity(resource, movieEntity, retain: shouldRetain);
retained = shouldRetain;
}
if (widget.movieConfigurer != null) {
movieEntity.dynamicItem.reset();
await widget.movieConfigurer!(movieEntity);
}
if (!mounted || widget.resource.trim() != resource) {
if (retained) {
manager.releaseCachedSvgaEntity(resource);
}
return;
}
if (retained) {
_replaceRetainedResource(resource);
}
setState(() {
_loadedResource = resource;
_controller.videoItem = movieEntity;
@ -157,8 +175,29 @@ class _SCNetworkSvgaWidgetState extends State<SCNetworkSvgaWidget>
}
}
void _replaceRetainedResource(String resource) {
final previous = _retainedResource;
if (previous == resource) {
return;
}
if (previous != null) {
SCGiftVapSvgaManager().releaseCachedSvgaEntity(previous);
}
_retainedResource = resource;
}
void _releaseRetainedResource() {
final resource = _retainedResource;
if (resource == null) {
return;
}
_retainedResource = null;
SCGiftVapSvgaManager().releaseCachedSvgaEntity(resource);
}
@override
void dispose() {
_releaseRetainedResource();
_controller.dispose();
super.dispose();
}

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.6.0+160
version: 1.6.1+161
environment: