IMA SDK를 사용하면 웹사이트와 앱에 멀티미디어 광고를 쉽게 통합할 수 있습니다. IMA SDK는 모든 VAST 호환 광고 서버에서 광고를 요청하고 앱에서 광고 재생을 관리할 수 있습니다. IMA 클라이언트 측 SDK를 사용하면 콘텐츠 동영상 재생을 계속 제어할 수 있으며 SDK가 광고 재생을 처리합니다. 광고는 앱의 콘텐츠 동영상 플레이어 위에 배치된 별도의 동영상 플레이어에서 재생됩니다.
이 가이드에서는 Android VideoView를 사용하여 콘텐츠와 광고를 표시하는 IMA SDK를 빈 Android 스튜디오 프로젝트에 통합하는 방법을 보여줍니다. 완성된 샘플 통합을 따라하려면 GitHub에서 BasicExample을 다운로드하세요.
IMA 클라이언트 측 개요
IMA 클라이언트 측을 구현하려면 이 가이드에 설명된 네 가지 주요 SDK 구성요소가 필요합니다.
AdDisplayContainer
: IMA가 광고 UI 요소를 렌더링하고 Active View 및 Open Measurement를 비롯한 조회가능성을 측정하는 위치를 지정하는 컨테이너 객체입니다.AdsLoader
: 광고를 요청하고 광고 요청 응답의 이벤트를 처리하는 객체입니다. 애플리케이션의 전체 기간 동안 재사용할 수 있는 광고 로더는 하나만 인스턴스화해야 합니다.AdsRequest
: 광고 요청을 정의하는 객체입니다. 광고 요청은 VAST 광고 태그의 URL과 광고 크기와 같은 추가 매개변수를 지정합니다.AdsManager
: 광고 요청에 대한 응답을 포함하고, 광고 재생을 제어하며, SDK에서 실행된 광고 이벤트를 리슨하는 객체입니다.
기본 요건
1. 새 Android 스튜디오 프로젝트 만들기
Android 스튜디오 프로젝트를 만들려면 다음 단계를 완료하세요.
- Android 스튜디오를 시작합니다.
- Start a new Android Studio project를 선택합니다.
- Choose your project 페이지에서 Empty Activity 템플릿을 선택합니다.
- 다음을 클릭합니다.
- 프로젝트 구성 페이지에서 프로젝트 이름을 지정하고 언어로 Java를 선택합니다.
- 마침을 클릭합니다.
2. 프로젝트에 IMA SDK 추가
먼저 애플리케이션 수준 build.gradle 파일에서 종속 항목 섹션에 IMA SDK의 가져오기를 추가합니다. IMA SDK의 크기 때문에 여기에서 multidex를 구현하고 사용 설정합니다. 이는 minSdkVersion
가 20 이하로 설정된 앱에 필요합니다. 또한 새 compileOptions
를 추가하여 Java 버전 호환성 정보를 지정합니다.
android { namespace 'com.google.ads.interactivemedia.v3.samples.videoplayerapp' compileSdkVersion 34 compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } defaultConfig { applicationId "com.google.ads.interactivemedia.v3.samples.videoplayerapp" minSdkVersion 21 targetSdkVersion 34 multiDexEnabled true versionCode 1 versionName "1.0" } ... } dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.browser:browser:1.6.0' implementation 'androidx.media:media:1.6.0' implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.36.0' ... }
3. IMA SDK 필수 권한 추가
IMA SDK가 광고를 요청하는 데 필요한 사용자 권한을 추가합니다.
app/src/main/AndroidManifest.xml<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.project name"> <!-- Required permissions for the IMA SDK --> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> ... </manifest>
4. 앱 레이아웃 업데이트
콘텐츠와 광고를 모두 재생하는 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: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:textSize="@dimen/font_size" /> </FrameLayout> </LinearLayout>
5. IMA를 기본 활동으로 가져오기
IMA SDK의 import 문을 추가합니다. 그런 다음 MyActivity
클래스를 업데이트하여 AppCompatActivity
를 확장합니다. AppCompatActivity
클래스를 사용하면 이전 Android 기기에서 최신 플랫폼 기능을 지원할 수 있습니다. 그런 다음 앱에서 사용될 비공개 변수 집합을 추가합니다.
import android.content.Context; import android.media.AudioManager; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.MediaController; import android.widget.VideoView; 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.AdsManagerLoadedEvent; 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; ... 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&impl=s&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 View playButton; private VideoAdPlayerAdapter videoAdPlayerAdapter; }
6. VideoAdPlayerAdapter 클래스 만들기
VideoView
로 VideoAdPlayerAdapter
클래스를 만들고 IMA의
VideoAdPlayer
인터페이스에 맞게 조정합니다. 이 클래스는 콘텐츠 및 광고 재생을 처리하며 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; } }
7. VideoAdPlayer 메서드 재정의
다음 VideoAdPlayer
메서드를 재정의합니다.
playAd()
메서드는 콘텐츠 또는 광고 URL을 설정하고 미디어가 로드되면 재생을 시작하도록 리스너를 설정합니다.
/** Example implementation of IMA's VideoAdPlayer interface. */ public class VideoAdPlayerAdapter implements VideoAdPlayer { ... @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); }
8. 광고 추적 설정
광고 이벤트를 등록하려면 콘텐츠와 광고가 진행될 때 VideoAdPlayerCallback.onAdProgress
를 호출해야 합니다. 이를 지원하려면 설정된 간격으로 onAdProgress()
를 호출하는 타이머를 설정하세요.
/** Example implementation of IMA's VideoAdPlayer interface. */ public class VideoAdPlayerAdapter implements VideoAdPlayer { ... 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"); break; case MediaPlayer.MEDIA_ERROR_TIMED_OUT: Log.e(LOGTAG, "notifyImaSdkAboutAdError: MEDIA_ERROR_TIMED_OUT"); break; default: break; } 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); } }
9. onCreate
메서드에서 IMA 시작
onCreate
메서드를 덮어쓰고 IMA를 시작하는 데 필요한 변수 할당을 추가합니다. 이 단계에서는 다음을 만듭니다.
ImaSdkSettings
AdsLoader
이 단계에서는 이 가이드의 뒷부분에서 만드는 클래스인 VideoAdPlayerAdapter
도 만듭니다.
마지막으로 재생 버튼을 설정하여 광고를 요청한 다음 클릭 시 숨깁니다.
app/src/main/java/com/example/project name/MyActivity.java... public class MyActivity extends AppCompatActivity { ... private VideoView videoPlayer; private MediaController mediaController; private View playButton; private VideoAdPlayerAdapter videoAdPlayerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); // 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. ViewGroup videoPlayerContainer = findViewById(R.id.videoPlayerContainer); AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); videoAdPlayerAdapter = new VideoAdPlayerAdapter(videoPlayer, audioManager); sdkFactory = ImaSdkFactory.getInstance(); AdDisplayContainer adDisplayContainer = ImaSdkFactory.createAdDisplayContainer(videoPlayerContainer, videoAdPlayerAdapter); // Create an AdsLoader. ImaSdkSettings settings = sdkFactory.createImaSdkSettings(); adsLoader = sdkFactory.createAdsLoader(this, settings, adDisplayContainer); // When the play button is clicked, request ads and hide the button. playButton = findViewById(R.id.playButton); playButton.setOnClickListener( view -> { videoPlayer.setVideoPath(SAMPLE_VIDEO_URL); requestAds(SAMPLE_VAST_TAG_URL); view.setVisibility(View.GONE); }); } }
10. AdsLoader 리스너 추가
addAdErrorListener
및 addAdsLoadedListener
의 리스너를 추가합니다. AdsLoadedListener
에서 AdsManager
를 만들고 AdsManager
오류 리스너를 설정합니다.
@Override protected void onCreate(Bundle savedInstanceState) { ... // Create an AdsLoader. ImaSdkSettings settings = sdkFactory.createImaSdkSettings(); adsLoader = sdkFactory.createAdsLoader(this, settings, adDisplayContainer); // 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( new AdsLoader.AdsLoadedListener() { @Override public void onAdsManagerLoaded(AdsManagerLoadedEvent 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(); } }); } });
11. IMA 광고 이벤트 처리
AdsManager.addAdEventListener
를 사용하여 IMA 광고 이벤트를 수신 대기합니다. 스위치 문을 사용하여 다음 IMA 이벤트에 대한 작업을 설정합니다.
코드 스니펫에는 이벤트 사용 방법에 관한 자세한 내용이 포함된 주석이 있습니다. 이벤트가 설정되면 AdsManager.init()
를 호출할 수 있습니다.
adsLoader.addAdsLoadedListener( new AdsLoader.AdsLoadedListener() { @Override public void onAdsManagerLoaded(AdsManagerLoadedEvent adsManagerLoadedEvent) { ... 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(); break; case CONTENT_PAUSE_REQUESTED: // AdEventType.CONTENT_PAUSE_REQUESTED is fired when you // should pause your content and start playing an ad. pauseContentForAds(); break; case CONTENT_RESUME_REQUESTED: // AdEventType.CONTENT_RESUME_REQUESTED is fired when the ad // you should play your content. resumeContent(); break; case ALL_ADS_COMPLETED: // Calling adsManager.destroy() triggers the function // VideoAdPlayer.release(). adsManager.destroy(); adsManager = null; break; 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. break; default: break; } } }); AdsRenderingSettings adsRenderingSettings = ImaSdkFactory.getInstance().createAdsRenderingSettings(); adsManager.init(adsRenderingSettings); }
12. 광고와 콘텐츠 간의 전환 처리
이 섹션에서는 이전 단계에서 참조된 pauseContentForAds
및 resumeContent
메서드를 만듭니다. 이러한 메서드는 플레이어를 재사용하여 콘텐츠와 광고를 모두 재생합니다. 광고 시점 후 재생을 재개하려면 콘텐츠 위치를 계속 추적해야 합니다.
/** Main activity. */ public class MyActivity extends AppCompatActivity { ... 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()); } }
13. 광고 요청
이제 requestAds
메서드를 추가하여 AdsRequest
를 빌드하고 이를 사용하여 AdsLoader.requestAds()
를 호출합니다.
/** Main activity. */ public class MyActivity extends AppCompatActivity { ... 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를 사용하여 광고를 요청하고 게재합니다. 추가 SDK 기능에 관해 알아보려면 다른 가이드 또는 GitHub의 샘플을 참고하세요.