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

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

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

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

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

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

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

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

/21775744923/example/app-open

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

מידע נוסף על אופן הפעולה של מודעות הבדיקה ב-Google Mobile Ads SDK זמין במאמר הפעלת מודעות בדיקה.

הרחבת הכיתה Application

יוצרים כיתה חדשה שמרחיבה את הכיתה Application ומוסיפים את הקוד הבא כדי להפעיל את Google Mobile Ads SDK כשהאפליקציה מתחילה לפעול.

Java

/** Application class that initializes, loads and show ads when activities change states. */
public class MyApplication extends Application {

  @Override
  public void onCreate() {
    super.onCreate();
    new Thread(
            () -> {
              // Initialize the Google Mobile Ads SDK on a background thread.
              MobileAds.initialize(this, initializationStatus -> {});
            })
        .start();
  }
}

Kotlin

/** Application class that initializes, loads and show ads when activities change states. */
class MyApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    val backgroundScope = CoroutineScope(Dispatchers.IO)
    backgroundScope.launch {
      // Initialize the Google Mobile Ads SDK on a background thread.
      MobileAds.initialize(this@MyApplication) {}
    }
  }
}

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

בשלב הבא, מוסיפים את הקוד הבא ל-AndroidManifest.xml:

<!-- TODO: Update to reference your actual package name. -->
<application
    android:name="com.google.android.gms.example.appopendemo.MyApplication" ...>
...
</application>

הטמעת רכיב השירות

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

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

Java

public class MyApplication extends Application {
  // ...
  /** Inner class that loads and shows app open ads. */
  private class AppOpenAdManager {
    private static final String LOG_TAG = "AppOpenAdManager";
    private static final String AD_UNIT_ID = "/21775744923/example/app-open";

    private AppOpenAd appOpenAd = null;
    private boolean isLoadingAd = false;
    private boolean isShowingAd = false;

    /** Constructor. */
    public AppOpenAdManager() {}

    /** Request an ad. */
    private void loadAd(Context context) {
      // We will implement this later.
    }

    /** Check if ad exists and can be shown. */
    private boolean isAdAvailable() {
      return appOpenAd != null;
    }
  }
}

Kotlin

private const val String LOG_TAG = "AppOpenAdManager"
private const val String AD_UNIT_ID = "/21775744923/example/app-open"

public class MyApplication extends Application {
  // ...
  /** Inner class that loads and shows app open ads. */
  private inner class AppOpenAdManager {
    private var appOpenAd: AppOpenAd? = null
    private var isLoadingAd = false
    var isShowingAd = false

    /** Request an ad. */
    fun loadAd(context: Context) {
      // We will implement this later.
    }

    /** Check if ad exists and can be shown. */
    private fun isAdAvailable(): Boolean {
      return appOpenAd != null
    }
  }
}

עכשיו, כשיש לכם סוג שירות, תוכלו ליצור מופע שלו בכיתה MyApplication:

Java

public class MyApplication extends Application {

  private AppOpenAdManager appOpenAdManager;

  @Override
  public void onCreate() {
    super.onCreate();
    new Thread(
            () -> {
              // Initialize the Google Mobile Ads SDK on a background thread.
              MobileAds.initialize(this, initializationStatus -> {});
            })
        .start();
    appOpenAdManager = new AppOpenAdManager(this);
  }
}

Kotlin

class MyApplication : Application() {

  private lateinit var appOpenAdManager: AppOpenAdManager

  override fun onCreate() {
    super.onCreate()
    val backgroundScope = CoroutineScope(Dispatchers.IO)
    backgroundScope.launch {
      // Initialize the Google Mobile Ads SDK on a background thread.
      MobileAds.initialize(this@MyApplication) {}
    }
    appOpenAdManager = AppOpenAdManager()
  }
}

טעינת מודעה

השלב הבא הוא למלא את השיטה loadAd() ולטפל בקריאות החוזרות (callbacks) של טעינת המודעות.

Java

private class AppOpenAdManager {
  // ...
  /** Request an ad. */
  public void loadAd(Context context) {
    // Do not load ad if there is an unused ad or one is already loading.
    if (isLoadingAd || isAdAvailable()) {
      return;
    }

    isLoadingAd = true;
    AdRequest request = new AdRequest.Builder().build();
    AppOpenAd.load(
        context, AD_UNIT_ID, request,
        AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
        new AppOpenAdLoadCallback() {
          @Override
          public void onAdLoaded(AppOpenAd ad) {
            // Called when an app open ad has loaded.
            Log.d(LOG_TAG, "Ad was loaded.");
            appOpenAd = ad;
            isLoadingAd = false;
            loadTime = (new Date()).getTime();
          }

          @Override
          public void onAdFailedToLoad(LoadAdError loadAdError) {
            // Called when an app open ad has failed to load.
            Log.d(LOG_TAG, loadAdError.getMessage());
            isLoadingAd = false;
          }
        });
  }
  // ...
}

Kotlin

private inner class AppOpenAdManager {
  // ...
  /** Request an ad. */
  fun loadAd(context: Context) {
    // Do not load ad if there is an unused ad or one is already loading.
    if (isLoadingAd || isAdAvailable()) {
      return
    }

    isLoadingAd = true
    val request = AdRequest.Builder().build()
    AppOpenAd.load(
        context, AD_UNIT_ID, request,
        AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT,
        object : AppOpenAdLoadCallback() {

          override fun onAdLoaded(ad: AppOpenAd) {
            // Called when an app open ad has loaded.
            Log.d(LOG_TAG, "Ad was loaded.")
            appOpenAd = ad
            isLoadingAd = false
            loadTime = Date().time
          }

          override fun onAdFailedToLoad(loadAdError: LoadAdError) {
            // Called when an app open ad has failed to load.
            Log.d(LOG_TAG, loadAdError.message)
            isLoadingAd = false;
          }
        })
  }
  // ...
}

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

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

הקוד הבא מראה איך להציג מודעה ולאחר מכן לטעון אותה מחדש:

Java

public class MyApplication extends Application {
  // ...
  /** Interface definition for a callback to be invoked when an app open ad is complete. */
  public interface OnShowAdCompleteListener {
    void onShowAdComplete();
  }

  private class AppOpenAdManager {
    // ...

    /** Shows the ad if one isn't already showing. */
    public void showAdIfAvailable(
        @NonNull final Activity activity,
        @NonNull OnShowAdCompleteListener onShowAdCompleteListener){
      // If the app open ad is already showing, do not show the ad again.
      if (isShowingAd) {
        Log.d(LOG_TAG, "The app open ad is already showing.");
        return;
      }

      // If the app open ad is not available yet, invoke the callback then load the ad.
      if (!isAdAvailable()) {
        Log.d(LOG_TAG, "The app open ad is not ready yet.");
        onShowAdCompleteListener.onShowAdComplete();
        loadAd(activity);
        return;
      }

      appOpenAd.setFullScreenContentCallback(
          new FullScreenContentCallback() {

            @Override
            public void onAdDismissedFullScreenContent() {
              // Called when fullscreen content is dismissed.
              // Set the reference to null so isAdAvailable() returns false.
              Log.d(LOG_TAG, "Ad dismissed fullscreen content.");
              appOpenAd = null;
              isShowingAd = false;

              onShowAdCompleteListener.onShowAdComplete();
              loadAd(activity);
            }

            @Override
            public void onAdFailedToShowFullScreenContent(AdError adError) {
              // Called when fullscreen content failed to show.
              // Set the reference to null so isAdAvailable() returns false.
              Log.d(LOG_TAG, adError.getMessage());
              appOpenAd = null;
              isShowingAd = false;

              onShowAdCompleteListener.onShowAdComplete();
              loadAd(activity);
            }

            @Override
            public void onAdShowedFullScreenContent() {
              // Called when fullscreen content is shown.
              Log.d(LOG_TAG, "Ad showed fullscreen content.");
            }
          });
      isShowingAd = true;
      appOpenAd.show(activity);
    }
    // ...
  }
}

Kotlin

class MyApplication : Application() {
  // ...
  /** Interface definition for a callback to be invoked when an app open ad is complete. */
  interface OnShowAdCompleteListener {
    fun onShowAdComplete()
  }

  private inner class AppOpenAdManager {
    // ...

    /** Shows the ad if one isn't already showing. */
    fun showAdIfAvailable(
        activity: Activity,
        onShowAdCompleteListener: OnShowAdCompleteListener) {
      // If the app open ad is already showing, do not show the ad again.
      if (isShowingAd) {
        Log.d(LOG_TAG, "The app open ad is already showing.")
        return
      }

      // If the app open ad is not available yet, invoke the callback then load the ad.
      if (!isAdAvailable()) {
        Log.d(LOG_TAG, "The app open ad is not ready yet.")
        onShowAdCompleteListener.onShowAdComplete()
        loadAd(activity)
        return
      }

      appOpenAd?.setFullScreenContentCallback(
          object : FullScreenContentCallback() {

            override fun onAdDismissedFullScreenContent() {
              // Called when full screen content is dismissed.
              // Set the reference to null so isAdAvailable() returns false.
              Log.d(LOG_TAG, "Ad dismissed fullscreen content.")
              appOpenAd = null
              isShowingAd = false

              onShowAdCompleteListener.onShowAdComplete()
              loadAd(activity)
            }

            override fun onAdFailedToShowFullScreenContent(adError: AdError) {
              // Called when fullscreen content failed to show.
              // Set the reference to null so isAdAvailable() returns false.
              Log.d(LOG_TAG, adError.message)
              appOpenAd = null
              isShowingAd = false

              onShowAdCompleteListener.onShowAdComplete()
              loadAd(activity)
            }

            override fun onAdShowedFullScreenContent() {
              // Called when fullscreen content is shown.
              Log.d(LOG_TAG, "Ad showed fullscreen content.")
            }
          })
      isShowingAd = true
      appOpenAd?.show(activity)
    }
    // ...
  }
}

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

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

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

Java

private class AppOpenAdManager {
  // ...
  /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */
  private long loadTime = 0;

  // ...

  /** Utility method to check if ad was loaded more than n hours ago. */
  private boolean wasLoadTimeLessThanNHoursAgo(long numHours) {
    long dateDifference = (new Date()).getTime() - this.loadTime;
    long numMilliSecondsPerHour = 3600000;
    return (dateDifference < (numMilliSecondsPerHour * numHours));
  }

  /** Check if ad exists and can be shown. */
  public boolean isAdAvailable() {
    return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4);
  }
}

Kotlin

private inner class AppOpenAdManager {
  // ...
  /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */
  private var loadTime: Long = 0;

  // ...

  /** Utility method to check if ad was loaded more than n hours ago. */
  private fun wasLoadTimeLessThanNHoursAgo(numHours: Long): Boolean {
    val dateDifference: Long = Date().time - loadTime
    val numMilliSecondsPerHour: Long = 3600000
    return dateDifference < numMilliSecondsPerHour * numHours
  }

  /** Check if ad exists and can be shown. */
  private fun isAdAvailable(): Boolean {
    return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4)
  }
}

מעקב אחר הפעילות הנוכחית

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

Java

public class MyApplication extends Application implements ActivityLifecycleCallbacks {

  private Activity currentActivity;

  @Override
  public void onCreate() {
    super.onCreate();
    this.registerActivityLifecycleCallbacks(this);
    // ...
  }

  /** ActivityLifecycleCallback methods. */
  @Override
  public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}

  @Override
  public void onActivityStarted(Activity activity) {
    currentActivity = activity
  }

  @Override
  public void onActivityResumed(Activity activity) {}

  @Override
  public void onActivityStopped(Activity activity) {}

  @Override
  public void onActivityPaused(Activity activity) {}

  @Override
  public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}

  @Override
  public void onActivityDestroyed(Activity activity) {}
}

Kotlin

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

  private var currentActivity: Activity? = null

  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(this)
    // ...
  }

  /** ActivityLifecycleCallback methods. */
  override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}

  override fun onActivityStarted(activity: Activity) {
    currentActivity = activity
  }

  override fun onActivityResumed(activity: Activity) {}

  override fun onActivityPaused(activity: Activity) {}

  override fun onActivityStopped(activity: Activity) {}

  override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}

  override fun onActivityDestroyed(activity: Activity) {}
}

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

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

מוסיפים את הספריות לקובץ gradle

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

Kotlin

  dependencies {
    implementation("com.google.android.gms:play-services-ads:23.6.0")
    implementation("androidx.lifecycle:lifecycle-process:2.8.3")
  }

Groovy

  dependencies {
    implementation 'com.google.android.gms:play-services-ads:23.6.0'
    implementation 'androidx.lifecycle:lifecycle-process:2.8.3'
  }

הטמעת ממשק הצופה במחזור החיים

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

מטמיעים את האירוע onStart כדי להציג את המודעה כשהאפליקציה פתוחה.

Java

public class MyApplication extends Application
    implements ActivityLifecycleCallbacks, LifecycleObserver {
  // ...
  @Override
  public void onCreate() {
    super.onCreate();
    this.registerActivityLifecycleCallbacks(this);
    new Thread(
            () -> {
              // Initialize the Google Mobile Ads SDK on a background thread.
              MobileAds.initialize(this, initializationStatus -> {});
            })
        .start();
    ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    appOpenAdManager = new AppOpenAdManager();
  }

  /** LifecycleObserver method that shows the app open ad when the app moves to foreground. */
  @OnLifecycleEvent(Event.ON_START)
  protected void onMoveToForeground() {
    // Show the ad (if available) when the app moves to foreground.
    appOpenAdManager.showAdIfAvailable(currentActivity);
  }

  /** Show the ad if one isn't already showing. */
  private void showAdIfAvailable(@NonNull final Activity activity) {
      showAdIfAvailable(
          activity,
          new OnShowAdCompleteListener() {
            @Override
            public void onShowAdComplete() {
              // Empty because the user will go back to the activity that shows the ad.
            }
          });
  }
}

Kotlin

class MyApplication : Application(),
    Application.ActivityLifecycleCallbacks, LifecycleObserver {
  // ...
  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(this)
    val backgroundScope = CoroutineScope(Dispatchers.IO)
    backgroundScope.launch {
      // Initialize the Google Mobile Ads SDK on a background thread.
      MobileAds.initialize(this@MyApplication) {}
    }
    ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    appOpenAdManager = AppOpenAdManager()
  }

  /** LifecycleObserver method that shows the app open ad when the app moves to foreground. */
  @OnLifecycleEvent(Lifecycle.Event.ON_START)
  fun onMoveToForeground() {
    // Show the ad (if available) when the app moves to foreground.
    currentActivity?.let {
      appOpenAdManager.showAdIfAvailable(it)
    }
  }

  /** Show the ad if one isn't already showing. */
  fun showAdIfAvailable(activity: Activity) {
    showAdIfAvailable(
        activity,
        object : OnShowAdCompleteListener {
          override fun onShowAdComplete() {
            // Empty because the user will go back to the activity that shows the ad.
          }
        })
  }
}

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

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

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

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

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

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

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

דוגמאות ב-GitHub

  • דוגמה למודעות בפתיחת אפליקציה: Java | Kotlin

השלבים הבאים

כדאי לעיין בנושאים הבאים: