Как настроить IMA SDK

Выберите платформу: HTML5 Android iOS tvOS

С помощью IMA SDK можно легко интегрировать мультимедийные объявления в сайты и приложения. IMA SDK могут запрашивать объявления у любого совместимого с VAST сервера объявлений и управлять воспроизведением рекламы в ваших приложениях. При использовании клиентских IMA SDK вы управляете воспроизведением видеоконтента, а SDK отвечает за воспроизведение рекламы. Объявления показываются в отдельном видеопроигрывателе, расположенном поверх видеопроигрывателя контента приложения.

В этом руководстве рассказывается, как интегрировать IMA SDK в пустой проект Android Studio, используя Android VideoView для показа контента и рекламы. Чтобы ознакомиться с готовым примером интеграции, скачайте BasicExample с GitHub.

Общие сведения о реализации IMA на стороне клиента

В клиентской IMA SDK есть четыре основных компонента, которые описаны в этом руководстве:

  • AdDisplayContainer – объект-контейнер, который указывает, где IMA отображает элементы интерфейса объявления и измеряет видимость, включая Active View и Open Measurement.
  • AdsLoader: объект, который запрашивает объявления и обрабатывает события из ответов на запросы объявлений. В приложении должен быть только один загрузчик объявлений, который можно использовать многократно.
  • AdsRequest – объект, определяющий запрос объявлений. В запросах объявлений указывается URL тега объявления VAST, а также дополнительные параметры, например размеры объявления.
  • AdsManager: объект, который содержит ответ на запрос объявлений, управляет воспроизведением объявлений и отслеживает события объявлений, активируемые SDK.

Требования

1. Создайте проект в Android Studio

Чтобы создать проект Android Studio, выполните следующие действия:

  1. Запустите Android Studio.
  2. Выберите Start a new Android Studio project (Создать проект Android Studio).
  3. На странице Choose your project (Выберите проект) выберите шаблон Empty Activity (Пустой объект activity).
  4. Нажмите Далее.
  5. На странице Настройте проект укажите название проекта и выберите язык Java.
  6. Нажмите Готово.

2. Как добавить IMA SDK в проект

Сначала в файле build.gradle на уровне приложения добавьте импорт IMA SDK в раздел зависимостей. Также добавьте новый тег compileOptions, чтобы указать информацию о совместимости с версией Java и включить десахаризацию библиотеки.

Для IMA SDK требуется включить десахаризацию библиотеки. Для этого задайте coreLibraryDesugaringEnabled true и добавьте 'com.android.tools:desugar_jdk_libs' в качестве зависимости в файле build.gradle. Подробнее о доступных через десахаризацию API Java 11+ со спецификацией nio…

apply plugin: 'com.android.application'

android {
    namespace = 'com.google.ads.interactivemedia.v3.samples.videoplayerapp'
    compileSdk = 36

    // Java 17 required by Gradle 8+
    compileOptions {
        // Required by IMA SDK v3.37.0+
        coreLibraryDesugaringEnabled = true

        // Java 17 required by Gradle 8+
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    defaultConfig {
        applicationId = "com.google.ads.interactivemedia.v3.samples.videoplayerapp"
        minSdkVersion(24)
        targetSdkVersion(36)
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        release {
            minifyEnabled = true
            proguardFiles(getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro')
        }
    }
}

repositories {
    google()
    mavenCentral()
}

dependencies {
    coreLibraryDesugaring('com.android.tools:desugar_jdk_libs:2.1.5')
    implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.3.21'))
    implementation('androidx.appcompat:appcompat:1.8.0')
    implementation('androidx.browser:browser:1.10.0')
    implementation('androidx.media:media:1.8.0')
    implementation('com.google.ads.interactivemedia.v3:interactivemedia:3.40.0')
}

3. Обновите макет приложения

Измените макет приложения, чтобы добавить элемент VideoView для воспроизведения контента и рекламы:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MyActivity"
    tools:ignore="MergeRootFrame">

    <RelativeLayout
        android:background="#000000"
        android:fitsSystemWindows="true"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.4"
        android:orientation="vertical"
        android:id="@+id/videoPlayerContainer" >

        <VideoView
            android:id="@+id/videoView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <ImageButton
            android:id="@+id/playButton"
            android:contentDescription="@string/play_description"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/ic_action_play_over_video"
            android:background="@null" />

    </RelativeLayout>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.6"
        android:id="@+id/videoDescription" >

        <TextView
            android:id="@+id/playerDescription"
            android:text="@string/app_name"
            android:textAlignment="center"
            android:gravity="center_horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingVertical="@dimen/font_size"
            android:textSize="@dimen/font_size" />
    </FrameLayout>

</LinearLayout>

4. Импортируйте IMA в основную активность

Добавьте операторы импорта для IMA SDK:

import android.content.Context;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.MediaController;
import android.widget.VideoView;
import androidx.appcompat.app.AppCompatActivity;
import com.google.ads.interactivemedia.v3.api.AdDisplayContainer;
import com.google.ads.interactivemedia.v3.api.AdErrorEvent;
import com.google.ads.interactivemedia.v3.api.AdEvent;
import com.google.ads.interactivemedia.v3.api.AdsLoader;
import com.google.ads.interactivemedia.v3.api.AdsManager;
import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings;
import com.google.ads.interactivemedia.v3.api.AdsRequest;
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
import java.util.Arrays;

Обновите класс MyActivity, чтобы продлить срок действия AppCompatActivity. Класс AppCompatActivity позволяет поддерживать новые функции платформы на более старых устройствах Android. Затем добавьте набор закрытых переменных, которые будут использоваться в приложении:

/** Main activity. */
public class MyActivity extends AppCompatActivity {

  private static final String LOGTAG = "IMABasicSample";
  private static final String SAMPLE_VIDEO_URL =
      "https://storage.googleapis.com/gvabox/media/samples/stock.mp4";

  /**
   * IMA sample tag for a single skippable inline video ad. See more IMA sample tags at
   * https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags
   */
  private static final String SAMPLE_VAST_TAG_URL =
      "https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/"
          + "single_preroll_skippable&sz=640x480&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast"
          + "&unviewed_position_start=1&env=vp&correlator=";

  // Factory class for creating SDK objects.
  private ImaSdkFactory sdkFactory;

  // The AdsLoader instance exposes the requestAds method.
  private AdsLoader adsLoader;

  // AdsManager exposes methods to control ad playback and listen to ad events.
  private AdsManager adsManager;

  // The saved content position, used to resumed content following an ad break.
  private int savedPosition = 0;

  // This sample uses a VideoView for content and ad playback. For production
  // apps, Android's Exoplayer offers a more fully featured player compared to
  // the VideoView.
  private VideoView videoPlayer;
  private MediaController mediaController;
  private VideoAdPlayerAdapter videoAdPlayerAdapter;
  private ImaSdkSettings imaSdkSettings;

5. Создайте класс VideoAdPlayerAdapter

Создайте класс VideoAdPlayerAdapter с VideoView и адаптируйте его к интерфейсу VideoAdPlayer IMA. Этот класс будет управлять воспроизведением контента и рекламы и содержать набор методов, которые должен реализовать видеопроигрыватель для использования IMA SDK:

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.util.Log;
import android.widget.VideoView;
import com.google.ads.interactivemedia.v3.api.AdPodInfo;
import com.google.ads.interactivemedia.v3.api.player.AdMediaInfo;
import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/** Example implementation of IMA's VideoAdPlayer interface. */
public class VideoAdPlayerAdapter implements VideoAdPlayer {

  private static final String LOGTAG = "IMABasicSample";
  private static final long POLLING_TIME_MS = 250;
  private static final long INITIAL_DELAY_MS = 250;
  private final VideoView videoPlayer;
  private final AudioManager audioManager;
  private final List<VideoAdPlayerCallback> videoAdPlayerCallbacks = new ArrayList<>();
  private Timer timer;
  private int adDuration;

  // The saved ad position, used to resumed ad playback following an ad click-through.
  private int savedAdPosition;
  private AdMediaInfo loadedAdMediaInfo;

  public VideoAdPlayerAdapter(VideoView videoPlayer, AudioManager audioManager) {
    this.videoPlayer = videoPlayer;
    this.videoPlayer.setOnCompletionListener(
        (MediaPlayer mediaPlayer) -> notifyImaOnContentCompleted());
    this.audioManager = audioManager;
  }

6. Переопределите методы VideoAdPlayer

Переопределите следующие методы VideoAdPlayer:

Метод playAd() задает URL контента или объявления и устанавливает прослушиватель, который запускает воспроизведение после загрузки медиаконтента.

@Override
public void addCallback(VideoAdPlayerCallback videoAdPlayerCallback) {
  videoAdPlayerCallbacks.add(videoAdPlayerCallback);
}

@Override
public void loadAd(AdMediaInfo adMediaInfo, AdPodInfo adPodInfo) {
  // This simple ad loading logic works because preloading is disabled. To support
  // preloading ads your app must maintain state for the currently playing ad
  // while handling upcoming ad downloading and buffering at the same time.
  // See the IMA Android preloading guide for more info:
  // https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/preload
  loadedAdMediaInfo = adMediaInfo;
}

@Override
public void pauseAd(AdMediaInfo adMediaInfo) {
  Log.i(LOGTAG, "pauseAd");
  savedAdPosition = videoPlayer.getCurrentPosition();
  stopAdTracking();
}

@Override
public void playAd(AdMediaInfo adMediaInfo) {
  videoPlayer.setVideoURI(Uri.parse(adMediaInfo.getUrl()));

  videoPlayer.setOnPreparedListener(
      mediaPlayer -> {
        adDuration = mediaPlayer.getDuration();
        if (savedAdPosition > 0) {
          mediaPlayer.seekTo(savedAdPosition);
        }
        mediaPlayer.start();
        startAdTracking();
      });
  videoPlayer.setOnErrorListener(
      (mediaPlayer, errorType, extra) -> notifyImaSdkAboutAdError(errorType));
  videoPlayer.setOnCompletionListener(
      mediaPlayer -> {
        savedAdPosition = 0;
        notifyImaSdkAboutAdEnded();
      });
}

@Override
public void release() {
  // any clean up that needs to be done.
}

@Override
public void removeCallback(VideoAdPlayerCallback videoAdPlayerCallback) {
  videoAdPlayerCallbacks.remove(videoAdPlayerCallback);
}

@Override
public void stopAd(AdMediaInfo adMediaInfo) {
  Log.i(LOGTAG, "stopAd");
  stopAdTracking();
}

/** Returns current volume as a percent of max volume. */
@Override
public int getVolume() {
  return audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
      / audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
}

7. Как настроить отслеживание рекламы

Чтобы регистрировать события, связанные с рекламой, необходимо вызывать функцию VideoAdPlayerCallback.onAdProgress по мере воспроизведения контента и рекламы. Для этого настройте таймер, который будет вызывать onAdProgress() через заданный интервал.

private void startAdTracking() {
  Log.i(LOGTAG, "startAdTracking");
  if (timer != null) {
    return;
  }
  timer = new Timer();
  TimerTask updateTimerTask =
      new TimerTask() {
        @Override
        public void run() {
          VideoProgressUpdate progressUpdate = getAdProgress();
          notifyImaSdkAboutAdProgress(progressUpdate);
        }
      };
  timer.schedule(updateTimerTask, POLLING_TIME_MS, INITIAL_DELAY_MS);
}

private void notifyImaSdkAboutAdEnded() {
  Log.i(LOGTAG, "notifyImaSdkAboutAdEnded");
  savedAdPosition = 0;
  for (VideoAdPlayer.VideoAdPlayerCallback callback : videoAdPlayerCallbacks) {
    callback.onEnded(loadedAdMediaInfo);
  }
}

private void notifyImaSdkAboutAdProgress(VideoProgressUpdate adProgress) {
  for (VideoAdPlayer.VideoAdPlayerCallback callback : videoAdPlayerCallbacks) {
    callback.onAdProgress(loadedAdMediaInfo, adProgress);
  }
}

/**
 * @param errorType Media player's error type as defined at
 *     https://cs.android.com/android/platform/superproject/+/master:frameworks/base/media/java/android/media/MediaPlayer.java;l=4335
 * @return True to stop the current ad playback.
 */
private boolean notifyImaSdkAboutAdError(int errorType) {
  Log.i(LOGTAG, "notifyImaSdkAboutAdError");

  switch (errorType) {
    case MediaPlayer.MEDIA_ERROR_UNSUPPORTED ->
        Log.e(LOGTAG, "notifyImaSdkAboutAdError: MEDIA_ERROR_UNSUPPORTED");
    case MediaPlayer.MEDIA_ERROR_TIMED_OUT ->
        Log.e(LOGTAG, "notifyImaSdkAboutAdError: MEDIA_ERROR_TIMED_OUT");
    default -> {}
  }
  for (VideoAdPlayer.VideoAdPlayerCallback callback : videoAdPlayerCallbacks) {
    callback.onError(loadedAdMediaInfo);
  }
  return true;
}

public void notifyImaOnContentCompleted() {
  Log.i(LOGTAG, "notifyImaOnContentCompleted");
  for (VideoAdPlayer.VideoAdPlayerCallback callback : videoAdPlayerCallbacks) {
    callback.onContentComplete();
  }
}

private void stopAdTracking() {
  Log.i(LOGTAG, "stopAdTracking");
  if (timer != null) {
    timer.cancel();
    timer = null;
  }
}

@Override
public VideoProgressUpdate getAdProgress() {
  long adPosition = videoPlayer.getCurrentPosition();
  return new VideoProgressUpdate(adPosition, adDuration);
}

8. Инициализация IMA в методе onCreate

Переопределите метод onCreate и добавьте необходимые назначения переменных, чтобы инициализировать IMA. На этом этапе создайте следующие экземпляры:

  • ImaSdkSettings
  • AdsLoader
  • VideoAdPlayerAdapter
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_my);

  // Initialize the IMA SDK as early as possible when the app starts. If your app already
  // overrides Application.onCreate(), call this method inside the onCreate() method.
  // https://developer.android.com/topic/performance/vitals/launch-time#app-creation
  sdkFactory = ImaSdkFactory.getInstance();
  sdkFactory.initialize(this, getImaSdkSettings());

  // Create the UI for controlling the video view.
  mediaController = new MediaController(this);
  videoPlayer = findViewById(R.id.videoView);
  mediaController.setAnchorView(videoPlayer);
  videoPlayer.setMediaController(mediaController);

  // Create an ad display container that uses a ViewGroup to listen to taps.
  AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
  videoAdPlayerAdapter = new VideoAdPlayerAdapter(videoPlayer, audioManager);

  AdDisplayContainer adDisplayContainer =
      ImaSdkFactory.createAdDisplayContainer(
          findViewById(R.id.videoPlayerContainer), videoAdPlayerAdapter);

  // Create an AdsLoader.
  adsLoader = sdkFactory.createAdsLoader(this, getImaSdkSettings(), adDisplayContainer);

Настройте кнопку воспроизведения так, чтобы она запрашивала объявления, а затем скрывалась после нажатия:

// When the play button is clicked, request ads and hide the button.
View playButton = findViewById(R.id.playButton);
playButton.setOnClickListener(
    view -> {
      videoPlayer.setVideoPath(SAMPLE_VIDEO_URL);
      requestAds(SAMPLE_VAST_TAG_URL);
      view.setVisibility(View.GONE);
    });
Наконец, добавьте вспомогательный метод для получения ImaSdkSettings:
private ImaSdkSettings getImaSdkSettings() {
  if (imaSdkSettings == null) {
    imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
    // Set any IMA SDK settings here.
  }
  return imaSdkSettings;
}

9. Добавьте прослушиватели AdsLoader

Добавьте слушателей для addAdErrorListener и addAdsLoadedListener. В AdsLoadedListener создайте AdsManager и настройте прослушиватель ошибок AdsManager:

// Add listeners for when ads are loaded and for errors.
adsLoader.addAdErrorListener(
    new AdErrorEvent.AdErrorListener() {
      /** An event raised when there is an error loading or playing ads. */
      @Override
      public void onAdError(AdErrorEvent adErrorEvent) {
        Log.i(LOGTAG, "Ad Error: " + adErrorEvent.getError().getMessage());
        resumeContent();
      }
    });
adsLoader.addAdsLoadedListener(
    adsManagerLoadedEvent -> {
      // Ads were successfully loaded, so get the AdsManager instance. AdsManager has
      // events for ad playback and errors.
      adsManager = adsManagerLoadedEvent.getAdsManager();

      // Attach event and error event listeners.
      adsManager.addAdErrorListener(
          new AdErrorEvent.AdErrorListener() {
            /** An event raised when there is an error loading or playing ads. */
            @Override
            public void onAdError(AdErrorEvent adErrorEvent) {
              Log.e(LOGTAG, "Ad Error: " + adErrorEvent.getError().getMessage());
              String universalAdIds =
                  Arrays.toString(adsManager.getCurrentAd().getUniversalAdIds());
              Log.i(
                  LOGTAG,
                  "Discarding the current ad break with universal "
                      + "ad Ids: "
                      + universalAdIds);
              adsManager.discardAdBreak();
            }
          });

10. Как обрабатывать события объявлений IMA

Слушайте события объявлений IMA с помощью AdsManager.addAdEventListener. Используя оператор switch, настройте действия для следующих событий IMA:

Фрагмент кода содержит комментарии с дополнительной информацией о том, как использовать события. После настройки событий вызовите AdsManager.init():

  adsManager.addAdEventListener(
      new AdEvent.AdEventListener() {
        /** Responds to AdEvents. */
        @Override
        public void onAdEvent(AdEvent adEvent) {
          if (adEvent.getType() != AdEvent.AdEventType.AD_PROGRESS) {
            Log.i(LOGTAG, "Event: " + adEvent.getType());
          }
          // These are the suggested event types to handle. For full list of
          // all ad event types, see AdEvent.AdEventType documentation.
          switch (adEvent.getType()) {
            case LOADED ->
                // AdEventType.LOADED is fired when ads are ready to play.
                // This sample app uses the sample tag
                // single_preroll_skippable_ad_tag_url that requires calling
                // AdsManager.start() to start ad playback.
                // If you use a different ad tag URL that returns a VMAP or
                // an ad rules playlist, the adsManager.init() function will
                // trigger ad playback automatically and the IMA SDK will
                // ignore the adsManager.start().
                // It is safe to always call adsManager.start() in the
                // LOADED event.
                adsManager.start();
            case CONTENT_PAUSE_REQUESTED ->
                // AdEventType.CONTENT_PAUSE_REQUESTED is fired when you
                // should pause your content and start playing an ad.
                pauseContentForAds();
            case CONTENT_RESUME_REQUESTED ->
                // AdEventType.CONTENT_RESUME_REQUESTED is fired when the ad
                // you should play your content.
                resumeContent();
            case ALL_ADS_COMPLETED -> {
              // Calling adsManager.destroy() triggers the function
              // VideoAdPlayer.release().
              adsManager.destroy();
              adsManager = null;
            }
            case CLICKED -> {
              // When the user clicks on the Learn More button, the IMA SDK fires
              // this event, pauses the ad, and opens the ad's click-through URL.
              // When the user returns to the app, the IMA SDK calls the
              // VideoAdPlayer.playAd() function automatically.
            }
            default -> {}
          }
        }
      });
  AdsRenderingSettings adsRenderingSettings =
      ImaSdkFactory.getInstance().createAdsRenderingSettings();
  // Add any ads rendering settings here.
  // This init() only loads the UI rendering settings locally.
  adsManager.init(adsRenderingSettings);
});

11. Как обрабатывать переключение между объявлениями и контентом

В этом разделе создайте методы pauseContentForAds и resumeContent, на которые ссылаются предыдущие шаги. Эти методы позволяют использовать проигрыватель как для контента, так и для рекламы. Вам нужно будет отслеживать позицию контента, чтобы возобновить воспроизведение после рекламной паузы.

private void pauseContentForAds() {
  Log.i(LOGTAG, "pauseContentForAds");
  savedPosition = videoPlayer.getCurrentPosition();
  videoPlayer.stopPlayback();
  // Hide the buttons and seek bar controlling the video view.
  videoPlayer.setMediaController(null);
}

private void resumeContent() {
  Log.i(LOGTAG, "resumeContent");

  // Show the buttons and seek bar controlling the video view.
  videoPlayer.setVideoPath(SAMPLE_VIDEO_URL);
  videoPlayer.setMediaController(mediaController);
  videoPlayer.setOnPreparedListener(
      mediaPlayer -> {
        if (savedPosition > 0) {
          mediaPlayer.seekTo(savedPosition);
        }
        mediaPlayer.start();
      });
  videoPlayer.setOnCompletionListener(
      mediaPlayer -> videoAdPlayerAdapter.notifyImaOnContentCompleted());
}

12. Запросить объявления

Теперь добавьте метод requestAds, чтобы создать объект AdsRequest, и используйте его для вызова AdsLoader.requestAds():

private void requestAds(String adTagUrl) {
  // Create the ads request.
  AdsRequest request = sdkFactory.createAdsRequest();
  request.setAdTagUrl(adTagUrl);
  request.setContentProgressProvider(
      () -> {
        if (videoPlayer.getDuration() <= 0) {
          return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
        }
        return new VideoProgressUpdate(
            videoPlayer.getCurrentPosition(), videoPlayer.getDuration());
      });

  // Request the ad. After the ad is loaded, onAdsManagerLoaded() will be called.
  adsLoader.requestAds(request);
}

Теперь вы можете запрашивать и показывать объявления с помощью IMA SDK. Чтобы узнать больше о дополнительных функциях, ознакомьтесь с другими руководствами или примерами на GitHub.