|
| 1 | +--- |
| 2 | +title: 'Feature: Ad System' |
| 3 | +description: Learn about the provider-agnostic ad system for monetization. |
| 4 | +--- |
| 5 | +import { Card, CardGrid, Aside } from '@astrojs/starlight/components'; |
| 6 | + |
| 7 | +The mobile client includes a sophisticated, provider-agnostic ad system designed for monetization while ensuring architectural stability and a smooth user experience. |
| 8 | + |
| 9 | +## Core Architecture |
| 10 | + |
| 11 | +The ad system is built on a decoupled architecture that avoids tying the application to a single ad network SDK. |
| 12 | + |
| 13 | +<CardGrid> |
| 14 | + <Card title="Provider-Agnostic Design" icon="interoperability"> |
| 15 | + The system uses a generic `NativeAd` model and an abstract `AdProvider` interface. This allows the application to use different ad networks by creating a new implementation of the `AdProvider`. The initial implementation is for **Google AdMob**. |
| 16 | + </Card> |
| 17 | + <Card title="Lifecycle Management" icon="lifecycle"> |
| 18 | + A critical challenge with native ad SDKs is managing the lifecycle of ad objects, which can lead to crashes if not handled correctly. This system solves this by managing ad objects within a `StatefulWidget` (`AdmobNativeAdWidget`) and a central `AdCacheService`, preventing premature disposal of ads that are scrolled off-screen. |
| 19 | + </Card> |
| 20 | + <Card title="Performance via Caching" icon="cache"> |
| 21 | + The `AdCacheService` acts as a singleton that stores loaded native ads. When an ad slot scrolls into view, the app first checks the cache. This prevents redundant network requests, leading to smoother scrolling and reduced data usage. The cache is strategically cleared on major content refreshes to ensure ad relevance. |
| 22 | + </Card> |
| 23 | + <Card title="Platform Safety" icon="shield"> |
| 24 | + On platforms where native ad SDKs are not supported (like web), a `NoOpAdProvider` is used. This provider renders a visual placeholder instead of a real ad, which prevents `MissingPluginException` crashes at startup and allows for consistent UI testing across all platforms. |
| 25 | + </Card> |
| 26 | +</CardGrid> |
| 27 | + |
| 28 | +## Integration with the Feed |
| 29 | + |
| 30 | +The ad system is seamlessly integrated into any scrollable list of content, such as the main headlines feed or entity details pages. |
| 31 | + |
| 32 | +- **`FeedDecoratorService`**: This service is responsible for injecting `AdPlaceholder` objects into the feed's content list based on rules defined in the `RemoteConfig`. These placeholders are simple, stateless markers indicating where an ad should appear. |
| 33 | + |
| 34 | +- **`AdLoaderWidget`**: This `StatefulWidget` is rendered when an `AdPlaceholder` is in the list. It contains the logic to: |
| 35 | + 1. Check the `AdCacheService` for a cached ad corresponding to the placeholder's ID. |
| 36 | + 2. If no ad is cached, it requests a new one from the `AdService`. |
| 37 | + 3. It manages its own loading and error states, displaying a shimmer while loading or a placeholder on failure. |
| 38 | + 4. Once an ad is loaded, it is stored in the cache and displayed. |
| 39 | + |
| 40 | +- **Theme & Format Aware Ads**: The `AdService` requests ads with theme-aware styling (`AdThemeStyle`) and the correct template size (`HeadlineImageStyle`). This ensures that ads match the user's current theme (light/dark mode) and the visual density of the surrounding content. |
| 41 | + |
| 42 | +This architecture ensures that the complex, stateful logic of ad loading is encapsulated at the widget level, keeping the BLoC layer clean and focused on managing the primary content. |
0 commit comments