Skip to content

Commit 79eea98

Browse files
committed
feat(ads): implement AdCacheService for native ad caching
- Create a singleton AdCacheService to cache native ads - Add methods to get, set, and clear cached ads - Implement logging for ad cache operations - Add a method to print the current state of the ad cache - Ensure proper disposal of AdMob native ad resources when clearing the cache
1 parent 037dcc6 commit 79eea98

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

lib/ads/ad_cache_service.dart

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import 'package:flutter/foundation.dart';
2+
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/native_ad.dart';
3+
import 'package:logging/logging.dart';
4+
5+
/// {@template ad_cache_service}
6+
/// A singleton service for caching loaded native ad objects.
7+
///
8+
/// This service helps to prevent unnecessary re-loading of native ads
9+
/// when they scroll out of view and then back into view in a scrollable list.
10+
/// It stores [NativeAd] objects by a unique ID (typically the [AdPlaceholder.id])
11+
/// and allows for retrieval and clearing of cached ads.
12+
///
13+
/// Native ad objects (like `google_mobile_ads.NativeAd`) are stateful and
14+
/// resource-intensive. Caching them allows for smoother scrolling and
15+
/// reduces network requests, while still ensuring proper disposal when
16+
/// the cache is cleared (e.g., on a full feed refresh).
17+
/// {@endtemplate}
18+
class AdCacheService {
19+
/// Private constructor for the singleton pattern.
20+
AdCacheService._internal() : _logger = Logger('AdCacheService');
21+
22+
/// The single instance of [AdCacheService].
23+
static final AdCacheService _instance = AdCacheService._internal();
24+
25+
/// Factory constructor to provide the singleton instance.
26+
factory AdCacheService() => _instance;
27+
28+
final Logger _logger;
29+
30+
/// A map to store loaded native ad objects, keyed by their unique ID.
31+
///
32+
/// The value is nullable to allow for explicit removal of an ad from the cache.
33+
final Map<String, NativeAd?> _cache = {};
34+
35+
/// Retrieves a [NativeAd] from the cache using its [id].
36+
///
37+
/// Returns the cached [NativeAd] if found, otherwise `null`.
38+
NativeAd? getAd(String id) {
39+
final ad = _cache[id];
40+
if (ad != null) {
41+
_logger.info('Retrieved ad with ID "$id" from cache.');
42+
} else {
43+
_logger.info('Ad with ID "$id" not found in cache.');
44+
}
45+
return ad;
46+
}
47+
48+
/// Adds or updates a [NativeAd] in the cache.
49+
///
50+
/// If [ad] is `null`, it effectively removes the entry for [id].
51+
void setAd(String id, NativeAd? ad) {
52+
if (ad != null) {
53+
_cache[id] = ad;
54+
_logger.info('Cached ad with ID "$id".');
55+
} else {
56+
_cache.remove(id);
57+
_logger.info('Removed ad with ID "$id" from cache.');
58+
}
59+
}
60+
61+
/// Clears all cached native ad objects and disposes their native resources.
62+
///
63+
/// This method should be called when the feed is fully refreshed or
64+
/// when the application is closing to ensure all native ad resources
65+
/// are released.
66+
void clearAllAds() {
67+
_logger.info('Clearing all cached ads and disposing native resources.');
68+
for (final ad in _cache.values) {
69+
// Only dispose if the ad is an AdMob native ad.
70+
// Placeholder ads do not have native resources to dispose.
71+
if (ad?.provider == AdProviderType.admob) {
72+
// Cast to the specific AdMob NativeAd type to call dispose.
73+
// This is safe because we check the provider type.
74+
(ad!.adObject as dynamic).dispose();
75+
}
76+
}
77+
_cache.clear();
78+
_logger.info('All cached ads cleared.');
79+
}
80+
81+
/// For debugging: prints the current state of the cache.
82+
@visibleForTesting
83+
void printCacheState() {
84+
_logger.info('Current Ad Cache State:');
85+
if (_cache.isEmpty) {
86+
_logger.info(' Cache is empty.');
87+
} else {
88+
_cache.forEach((id, ad) {
89+
_logger.info(' ID: $id, Provider: ${ad?.provider}, Template: ${ad?.templateType}');
90+
});
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)