スクロール可能なフィードでバナー広告をリサイクルする

ニュースフィードやソーシャル フィードなどの長いリストや無限スクロール リストでは、アイテムのすべての位置にバナー広告を作成すると、メモリ使用量が増加し、UI の遅延が発生する可能性があります。パフォーマンスを向上させるには、バナー リサイクルを使用して、BannerAd インスタンスの小さな固定サイズのキャッシュを維持し、ユーザーがスクロールしたときに画面外の広告を再利用します。

このページでは、広告キャッシュのサイズ設定、BannerAd インスタンスが画面外にあるかどうかの確認、キャッシュに保存された広告をリストの位置に再割り当てする方法について説明します。

次の図は、画面外のバナー広告がキャッシュに戻り、ユーザーがスクロールしたときにリストの位置で再利用される様子を示しています。

バナー広告が画面外にスクロールして広告キャッシュに移動し、画面上のフィードの下部で再利用される様子を示す図。

前提条件

続行する前に、スクロール バナーにインライン アダプティブを使用します。

割り当てられたリストの位置を追跡する

ユーザーがスクロールすると、バナー リサイクルによって、限られた数の BannerAd インスタンスがメモリに保持され、これらのインスタンスがリスト内の位置の間で移動します。

再利用する広告を特定するには、キャッシュ内の各広告が占めるリストの位置を追跡します。

ウィジェットで次の状態を維持します。

  • BannerAd インスタンスの固定サイズのキャッシュ。キャッシュに設定したサイズは、一度にメモリに保持される広告の最大数です。
  • リストに広告が表示される頻度を決定する広告間隔。
  • 各 BannerAd インスタンスを、そのインスタンスが占有するリストの位置に関連付ける位置 Map クラス。広告をリサイクルするときは、この Map クラスのエントリを更新して、最新の位置を指すようにします。
  • 読み込まれた各広告のプラットフォーム解決済み AdSize オブジェクトを保存するサイズ Map クラス。広告が読み込まれると、インライン アダプティブ バナーの高さがわかります。

次の例では、キャッシュ内の各広告に割り当てられたリストの位置をトラッキングします。

// Maximum number of banner ad instances kept in the cache.
static const int _cacheSize = 8;

// Spacing between banner ads. For example, index 0, 6, 12, and so on
// display ads.
static const int _adInterval = 6;

// The cache of allocated BannerAd instances.
final List<BannerAd> _banners = [];

// Maps each BannerAd instance to its currently assigned list item position.
final Map<BannerAd, int> _bannerPositions = {};

// Stores the platform-resolved size of each loaded banner ad.
final Map<BannerAd, AdSize> _bannerSizes = {};

キャッシュサイズを選択する

キャッシュを、画面に同時に表示できる広告の最大数よりも大きくします。キャッシュがそのサイズよりも小さい場合、リサイクル用に選択された広告は引き続き表示されることが多く、アプリは広告を再利用するのではなく、追加の広告を割り当てます。

サイズを選択するには、次の操作を行います。

  1. ビューポートの高さを 1 つの広告間隔の高さで割って、画面に同時に表示される広告の数を推定します。
  2. 高速スクロール中にまだ分離している広告のスペースを追加。
  3. リサイクル用に選択された広告がまだ画面に表示されている場合は、キャッシュ サイズを増やします。

このページの例では、6 行ごとに広告を表示し、8 個のキャッシュを使用しています。この例では、画面に一度に表示される少数の広告のためのスペースが確保されています。

広告が画面に表示されているかどうかを確認する

プラットフォーム ビューをレンダリングするすべての Google Mobile Ads Flutter Plugin クラスは、AdWithView クラスを実装し、isMounted ゲッターを提供します。キャッシュに保存された広告をリストの位置に再割り当てする前に、isMounted ゲッターをチェックして、広告が画面外にスクロールされ、再利用しても安全であることを確認します。

バナー広告を取得して再利用する

リストが特定のポジションのバナーをリクエストした場合は、次の処理を行います。

  1. その位置にすでに割り当てられている広告がある場合は、その広告を再利用します。
  2. キャッシュが満杯でない場合は、広告をリクエストします。
  3. キャッシュがいっぱいの場合は、キャッシュに保存されている広告を選択し、広告を再割り当てする前に isMounted ゲッターが false 値であることを確認します。
  4. ウィジェットがウィジェット ツリーから削除されたら、キャッシュ内のすべての広告に対して dispose() メソッドを呼び出します。

次の例では、バナー広告を取得して再利用しています。

BannerAd _getRecycledBannerAd(BuildContext context, int bannerPosition) {
  // 1. If an ad is already mapped to this position, reuse it.
  final BannerAd? existingBanner = _bannerPositions.entries
      .firstWhereOrNull((entry) => entry.value == bannerPosition)
      ?.key;
  if (existingBanner != null) {
    return existingBanner;
  }

  // 2. If the cache is not full, allocate a new ad instance.
  if (_banners.length < _cacheSize) {
    final BannerAd newBanner = _createBannerAd(context);
    _banners.add(newBanner);
    _bannerPositions[newBanner] = bannerPosition;
    return newBanner;
  }

  // 3. Select an existing ad from the cache using modulo arithmetic.
  final BannerAd targetBanner = _banners[bannerPosition % _cacheSize];

  // 4. Verify the ad is detached from the screen before recycling.
  if (targetBanner.isMounted) {
    // If still on screen during fast scrolls, allocate a temporary instance.
    return _createBannerAd(context);
  } else {
    // Safe to reuse: reassign to the new position.
    _bannerPositions[targetBanner] = bannerPosition;
    return targetBanner;
  }
}

完全な例については、Google Mobile Ads Flutter Plugin の例をご覧ください。