מודעות בפתיחת האפליקציה

מודעות בפתיחת האפליקציה הן פורמט מודעה מיוחד שמיועד לבעלי אפליקציות שרוצים לייצר הכנסות ממסכי הטעינה של האפליקציה. מודעות בפתיחת אפליקציה ניתנות לסגירה בכל שלב, והן מיועדות להצגה כשהמשתמשים מעבירים את האפליקציה לחזית.

במודעות בפתיחת האפליקציה מוצג באופן אוטומטי אזור מיתוג קטן, כדי שהמשתמשים ידעו שהם נמצאים באפליקציה שלכם. הנה דוגמה למודעה בפתיחת האפליקציה:

דרישות מוקדמות

לפני שממשיכים, צריך לבצע את הפעולות הבאות:

תמיד צריך לבדוק באמצעות מודעות בדיקה

כשאתם מפתחים ובודקים את האפליקציות, חשוב להשתמש במודעות בדיקה ולא במודעות פעילות. אחרת, אנחנו עלולים להשעות את החשבון.

הדרך הכי קלה לטעון מודעות לבדיקה היא להשתמש במזהים הייעודיים של יחידות המודעות לבדיקה של מודעות מתגמלות ב-Android וב-iOS:

Android

ca-app-pub-3940256099942544/9257395921

iOS

ca-app-pub-3940256099942544/5575463023

הם הוגדרו במיוחד כדי להחזיר מודעות בדיקה לכל בקשה, ואתם יכולים להשתמש בהם באפליקציות שלכם בזמן התכנות, הבדיקה וניפוי הבאגים. רק חשוב להחליף אותם במזהה יחידת המודעות שלכם לפני פרסום האפליקציה.

הטמעה

השלבים העיקריים לשילוב מודעות בפתיחת האפליקציה הם:

  1. יוצרים מחלקת שירות (utility class) שטוענת מודעה לפני שצריך להציג אותה.
  2. טוענים מודעה.
  3. רושמים קריאות חוזרות (callbacks) ומציגים את המודעה.
  4. כדי להציג את המודעה באירועים שפועלים בחזית, צריך להירשם ל-AppStateEventNotifier.appStateStream.

יצירת מחלקת שירות (utility class)

יוצרים מחלקה חדשה בשם AppOpenAdManager כדי לטעון את המודעה. המחלקה הזו מנהלת משתנה מופע כדי לעקוב אחרי מודעה שנטענה ואחרי מזהה יחידת המודעות בכל פלטפורמה.

import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'dart:io' show Platform;

class AppOpenAdManager {
  
  String adUnitId = Platform.isAndroid
    ? 'ca-app-pub-3940256099942544/9257395921'
    : 'ca-app-pub-3940256099942544/5575463023';
  
  AppOpenAd? _appOpenAd;
  bool _isShowingAd = false;

  /// Load an AppOpenAd.
  void loadAd() {
    // We will implement this below.
  }

  /// Whether an ad is available to be shown.
  bool get isAdAvailable {
    return _appOpenAd != null;
  }
}

טעינת מודעה

המודעה בפתיחת האפליקציה צריכה להיות מוכנה לפני שהמשתמשים נכנסים לאפליקציה. כדי לעשות את זה, צריך להטמיע מחלקת שירות (utility class) שתשלח בקשות להצגת מודעות לפני שהמודעה צריכה להופיע.

כדי לטעון מודעה, משתמשים ב-method‏ load במחלקה AppOpenAd. שיטת הטעינה דורשת מזהה יחידת מודעות, מצב כיוון, אובייקט AdRequest ו-handler של השלמה שמופעל כשטעינת המודעה מצליחה או נכשלת. אובייקט AppOpenAd שנטען מסופק כפרמטר ב-handler של ההשלמה. בדוגמה הבאה אפשר לראות איך טוענים AppOpenAd.

public class AppOpenAdManager {
  ...

  /// Load an AppOpenAd.
  void loadAd() {
    AppOpenAd.load(
      adUnitId: adUnitId,
      adRequest: AdRequest(),
      adLoadCallback: AppOpenAdLoadCallback(
        onAdLoaded: (ad) {
          _appOpenAd = ad;
        },
        onAdFailedToLoad: (error) {
          print('AppOpenAd failed to load: $error');
          // Handle the error.
        },
      ),
    );
  }
}

הצגת המודעה וטיפול בקריאות חוזרות (callbacks) במסך מלא

לפני הצגת המודעה, צריך לרשום FullScreenContentCallback לכל אירוע מודעה שרוצים להאזין לו.

public class AppOpenAdManager {
  ...

  public void showAdIfAvailable() {
    if (!isAdAvailable) {
      print('Tried to show ad before available.');
      loadAd();
      return;
    }
    if (_isShowingAd) {
      print('Tried to show ad while already showing an ad.');
      return;
    }
    // Set the fullScreenContentCallback and show the ad.
    _appOpenAd!.fullScreenContentCallback = FullScreenContentCallback(
      onAdShowedFullScreenContent: (ad) {
        _isShowingAd = true;
        print('$ad onAdShowedFullScreenContent');
      },
      onAdFailedToShowFullScreenContent: (ad, error) {
        print('$ad onAdFailedToShowFullScreenContent: $error');
        _isShowingAd = false;
        ad.dispose();
        _appOpenAd = null;
      },
      onAdDismissedFullScreenContent: (ad) {
        print('$ad onAdDismissedFullScreenContent');
        _isShowingAd = false;
        ad.dispose();
        _appOpenAd = null;
        loadAd();
      },
    );
  }
}

אם משתמש חוזר לאפליקציה אחרי שיצא ממנה בלחיצה על מודעה בפתיחת אפליקציה, חשוב לוודא שלא תוצג לו מודעה נוספת בפתיחת אפליקציה.

האזנה לאירועים של אפליקציות שפועלות בחזית

כדי לקבל התראות על אירועים של אפליקציות שפועלות בחזית, צריך להירשם ל-AppStateEventNotifier.appStateStream ולהאזין לאירועים של foreground.

import 'package:app_open_example/app_open_ad_manager.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

/// Listens for app foreground events and shows app open ads.
class AppLifecycleReactor {
  final AppOpenAdManager appOpenAdManager;

  AppLifecycleReactor({required this.appOpenAdManager});

  void listenToAppStateChanges() {
    AppStateEventNotifier.startListening();
    AppStateEventNotifier.appStateStream
        .forEach((state) => _onAppStateChanged(state));
  }

  void _onAppStateChanged(AppState appState) {
    // Try to show an app open ad if the app is being resumed and
    // we're not already showing an app open ad.
    if (appState == AppState.foreground) {
      appOpenAdManager.showAdIfAvailable();
    }
  }
}

עכשיו אפשר להוסיף אתחול של AppLifecycleReactor ולהתחיל להאזין לשינויים במחזור החיים של האפליקציה. לדוגמה, מדף הבית:

import 'package:app_open_example/app_open_ad_manager.dart';
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

import 'app_lifecycle_reactor.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MobileAds.instance.initialize();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'App Open Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'App Open Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

/// Example home page for an app open ad.
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  late AppLifecycleReactor _appLifecycleReactor;

  @override
  void initState() {
    super.initState();
    
    AppOpenAdManager appOpenAdManager = AppOpenAdManager()..loadAd();
    _appLifecycleReactor = AppLifecycleReactor(
      appOpenAdManager: appOpenAdManager);
  }

כדאי להביא בחשבון את תפוגת התוקף של המודעות

כדי לוודא שלא תוצג מודעה פגת תוקף, הוסיפו חותמת זמן ל-AppOpenAdManager כדי שתוכלו לבדוק כמה זמן עבר מאז שטעינת המודעה. לאחר מכן, משתמשים בחותמת הזמן הזו כדי לבדוק אם המודעה עדיין בתוקף.

/// Utility class that manages loading and showing app open ads.
class AppOpenAdManager {
  ...
  
  /// Maximum duration allowed between loading and showing the ad.
  final Duration maxCacheDuration = Duration(hours: 4);

  /// Keep track of load time so we don't show an expired ad.
  DateTime? _appOpenLoadTime;
  
  ...

  /// Load an AppOpenAd.
  void loadAd() {
    AppOpenAd.load(
      adUnitId: adUnitId,
      orientation: AppOpenAd.orientationPortrait,
      adRequest: AdRequest(),
      adLoadCallback: AppOpenAdLoadCallback(
        onAdLoaded: (ad) {
          print('$ad loaded');
          _appOpenLoadTime = DateTime.now();
          _appOpenAd = ad;
        },
        onAdFailedToLoad: (error) {
          print('AppOpenAd failed to load: $error');
        },
      ),
    );
  }

  /// Shows the ad, if one exists and is not already being shown.
  ///
  /// If the previously cached ad has expired, this just loads and caches a
  /// new ad.
  void showAdIfAvailable() {
    if (!isAdAvailable) {
      print('Tried to show ad before available.');
      loadAd();
      return;
    }
    if (_isShowingAd) {
      print('Tried to show ad while already showing an ad.');
      return;
    }
    if (DateTime.now().subtract(maxCacheDuration).isAfter(_appOpenLoadTime!)) {
      print('Maximum cache duration exceeded. Loading another ad.');
      _appOpenAd!.dispose();
      _appOpenAd = null;
      loadAd();
      return;
    }
    // Set the fullScreenContentCallback and show the ad.
    _appOpenAd!.fullScreenContentCallback = FullScreenContentCallback(...);
    _appOpenAd!.show();
  }
}

הפעלות במצב התחלתי (cold start) ומסכי טעינה

ההנחיות עד עכשיו מתייחסות למצב שבו המודעות בפתיחת האפליקציה מוצגות רק כשהמשתמשים מעבירים את האפליקציה לחזית כשהיא מושהית בזיכרון. "הפעלות קרות" מתרחשות כשהאפליקציה מופעלת אבל לא הושעתה קודם בזיכרון.

דוגמה להפעלה במצב התחלתי היא כשמשתמש פותח את האפליקציה בפעם הראשונה. בהפעלה במצב התחלתי, אין מודעה בפתיחת האפליקציה שנטענה מראש ומוכנה להצגה מיידית. העיכוב בין שליחת הבקשה להצגת מודעה לבין קבלת המודעה בחזרה יכול ליצור מצב שבו המשתמשים יכולים להשתמש באפליקציה לזמן קצר, ואז פתאום מופיעה מודעה שלא קשורה להקשר. כדאי להימנע מכך כי זה פוגע בחוויית המשתמש.

הדרך המועדפת להשתמש במודעות בפתיחת האפליקציה בהפעלות קרות היא להשתמש במסך טעינה כדי לטעון את נכסי המשחק או האפליקציה, ולהציג את המודעה רק ממסך הטעינה. אם האפליקציה סיימה להיטען והמשתמש הועבר לתוכן הראשי שלה, אל תציגו את המודעה.

שיטות מומלצות

מודעות בפתיחת האפליקציה עוזרות לכם לייצר הכנסות ממסך הטעינה של האפליקציה, כשהאפליקציה מופעלת בפעם הראשונה ובמהלך מעבר בין אפליקציות. עם זאת, חשוב לזכור את השיטות המומלצות כדי שהמשתמשים ייהנו מהשימוש באפליקציה. מומלץ:

  • הציגו את המודעה הראשונה בפתיחת האפליקציה אחרי שהמשתמשים השתמשו באפליקציה כמה פעמים.
  • הציגו מודעות בפתיחת האפליקציה בזמנים שבהם המשתמשים ממתינים שהאפליקציה תיטען.
  • אם יש לכם מסך טעינה מתחת למודעה בפתיחת האפליקציה, והטעינה של מסך הטעינה מסתיימת לפני שהמודעה נסגרת, כדאי לסגור את מסך הטעינה בגורם המטפל באירועים onAdDismissedFullScreenContent.

דוגמה פתורה ב-GitHub

בדמו של ה-API אפשר לראות את הדוגמה המלאה.