IMA SDK를 사용하면 멀티미디어 광고를 웹사이트와 앱에 쉽게 통합할 수 있습니다. IMA SDK는 원하는 형식에서 광고를 <ph type="x-smartling-placeholder"></ph> VAST 호환 광고 서버를 사용하고 앱에서 광고 재생을 관리할 수 있습니다. IMA 클라이언트 측 SDK를 사용하면 SDK는 광고 재생을 처리하는 동안 콘텐츠 동영상 재생은 계속 제어할 수 있습니다. 다음 시간 동안 광고 재생 앱의 콘텐츠 동영상 플레이어 위에 배치된 별도의 동영상 플레이어입니다.
이 가이드에서는 콘텐츠와 광고를 표시할 수 있습니다. 완성된 샘플과 함께 통합하려면 GitHub의 BasicExample
<ph type="x-smartling-placeholder">를 통해 개인정보처리방침을 정의할 수 있습니다.IMA 클라이언트 측 개요
IMA 클라이언트 측 구현에는 네 가지 주요 SDK 구성요소가 필요하며 가이드:
AdDisplayContainer
: 광고가 렌더링되는 컨테이너 객체입니다.AdsLoader
: 광고를 요청하고 광고 요청 응답의 이벤트를 처리하는 객체입니다. 다음과 같은 경우에만 하나의 광고 로더를 인스턴스화할 수 있습니다. 이는 애플리케이션의 수명 주기 동안 재사용될 수 있습니다.AdsRequest
: 광고 요청을 정의하는 객체입니다. 광고 요청은 VAST 광고 태그의 URL뿐 아니라 광고 크기와 같은 추가 매개변수AdsManager
: 광고 요청에 대한 응답을 포함하고, 광고 재생을 제어하며, 광고를 리슨하는 객체입니다. SDK에 의해 발생한 이벤트
기본 요건
1. 새 Android 스튜디오 프로젝트 만들기
Android 스튜디오 프로젝트를 만들려면 다음 단계를 완료하세요.
- Android 스튜디오를 시작합니다.
- Start a new Android Studio project를 선택합니다.
- Choose your project 페이지에서 Empty Activity 템플릿을 선택합니다.
- 다음을 클릭합니다.
- Configure your project 페이지에서 프로젝트 이름을 지정하고 언어로 Java를 선택합니다.
- 마침을 클릭합니다.
2. 프로젝트에 IMA SDK 추가
먼저 애플리케이션 수준의 build.gradle 파일에서 IMA SDK에 대한 가져오기를
종속 항목 섹션을 참조하세요. IMA SDK의 크기 때문에 여기에서 멀티덱스를 구현하고 사용 설정합니다. 이
는 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.35.1' ... }</ph>
3. IMA SDK 필수 권한 추가
광고를 요청하기 위해 IMA SDK에서 요구하는 사용자 권한을 추가합니다.
app/src/main/AndroidManifest.xml <ph type="x-smartling-placeholder"><?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></ph>
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></ph>
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의
<ph type="x-smartling-placeholder"></ph>
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
메서드를 덮어쓰고 필요한 변수 할당을 추가하여 시작합니다.
있습니다. 이 단계에서는 다음을 만듭니다.
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 광고 이벤트 처리
다음으로 IMA 광고 이벤트를 수신 대기합니다.
AdsManager.addAdEventListener
switch 문을 사용하여 다음 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를 사용하여 광고를 요청하고 표시합니다. 자세히 알아보려면 자세한 내용은 다른 가이드 또는 GitHub의 샘플