Google Admob 광고는 전면광고, 배너광고등 다양한 API가 존재하지만 이번 포스팅 에서는 보상형 광고만 다룰 예정이다.
보상형 광고 Google(구글 공식 Reference)
Admob SDK를 이용하기 위해서는 Gradle에 추가 해주고 앱 실행시점에 초기화를 해주어야 하지만 이 글에서 다룰 새로운 보상형 광고 API 는 굳이 초기화를 해주지 않아도 되는것 같다.. 나중에 실험해보야겠지만 일단 PASS
2020/05/04 - [Android] - Google Admob 광고 사용하기
구글 애드몹 광고는 유저에게 보여지기 이전에 로딩을 해놓아야 하는데 로딩이 오래걸리거나 아예 안되는 경우가 있다. 광고를 볼수 없다고 혹은 로딩이 계속 안된다고 유저분들로 부터 문의가 많이 들어와서 애드몹 측과도 얘기를 나누어 봤지만 개선이 안되는것 같다.. 다음은 조금이라도 문제를 개선해 보도록 내가 생각해낸 방법이다.
먼저 다음을 주목하자. 이번에 새로나온 보상형광고 API는 기존과 다르게 광고를 여러개 불러 올수 있도록 개선되었다.
여러 광고 캐싱: 이전 구현에서는 한 번에 하나의 광고만 로드할 수 있었으며 첫 번째 광고가 완료될 때까지 두 번째 광고를 로드할 수 없었습니다. 새 보상형 API를 사용하면 여러 광고를 동시에 캐싱할 수 있습니다.
from Google Admob SDK Reference
이를 활용해 광고를 미리 캐싱 해놓을 수 있도록 Wrapper 클래스를 작성해 보았다.
전체 코드를 보고자 하시는 분은 맨 아래에 기술되어 있습니다.
로직 설명
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/**
* List for caching reward ad to see without waiting
* + - - + - - +
* show <- | ad | ad | <- load
* + - - + - - +
*/
private val cachedRewardAd = LinkedList<RewardedAd>()
private fun initialize() {
cachedRewardAd.clear()
for(i in 0 until enableCaching) {
cachedRewardAd.addLast(createAndLoadRewardedAd())
}
}
|
cs |
리스트 안에 광고를 여러개 적재 해놓는 로직이다. 이로서 광고를 본후 다음광고가 로드될때 까지 기다리지 않고 연속적으로 광고를 시청할 수 있다. enableCaching 개 만큼 광고를 캐싱하며, createAndLoadRewardedAd() 함수를 통해 광고 객체를 받아온다.
다음은 리워드 광고 객체를 생성하는 createAndLoadRewardedAd() 함수이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
private fun createAndLoadRewardedAd(): RewardedAd {
val rewardedAd = if (isDeveloperMode) {
// 테스트 광고 단위
RewardedAd(activity, AdUnit.TEST.id)
} else {
//프로덕션 광고 단위
RewardedAd(activity, adUnit.id)
}
val adLoadCallback: RewardedAdLoadCallback = object : RewardedAdLoadCallback() {
override fun onRewardedAdLoaded() {
}
override fun onRewardedAdFailedToLoad(errorCode: Int) {
// Ad failed to load.
}
}
rewardedAd.loadAd(AdRequest.Builder().build(), adLoadCallback)
return rewardedAd
}
|
cs |
리워드 광고를 기본적으로 사용하는 법은 다음과 같다.
1
2
|
val rewardAd = RewardedAd(context, "광고 단위 ID") //인스턴스화
rewardedAd.loadAd(AdRequest.Builder().build(), adLoadCallback) //광고 동영상 로드
|
cs |
필자는 코드의 재사용성을 위해 "광고 단위 ID"를 매번 적지 않고 enum 객체를 활용하여 편리하게 사용할 수 있도록 만들어 두웠다.
1
2
3
4
5
6
|
enum class AdUnit(val id: String) {
TEST("ca-app-pub-3940256099942544/5224354917"),
A("your id"),
B("your id"),
C("your id"),
}
|
cs |
사용법은 아래에서 다시 기술하겠다.
또한 isDeveloperMode 라는 변수를 활용해서 개발시와 프로덕션 출시할때를 구분해 두웠다.
개발시 테스트 광고단위를 사용하지 않으면 Admob 계정이 정지될수 있다..(회사 짤려요)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
fun show(adCallback: PlayRewardAdCallback) {
if (cachedRewardAd.size == 0) {
// 광고 에러 다이얼로그
ErrorDialogGenerator.getInstance(activity).onAdmobNotLoaded(false)
return
}
var isRewarded = false
var rewardAmount = 1
val rewardAd = cachedRewardAd.first
if (rewardAd.isLoaded) {
val innerAdCallback: RewardedAdCallback = object : RewardedAdCallback() {
override fun onUserEarnedReward(p0: RewardItem) {
isRewarded = true
// 광고 시청이 끝나면 API는 보상 갯수를 RewardItem 객체를 통해 리턴해준다.
// 하지만, 개발 모드일때는 강제로 리워드 보상 갯수를 제한해준다. 이를 해주지 않으면 값이
// 10,20개 등등 들죽날죽하게 들어온다.
rewardAmount = if (isDeveloperMode) forcedRewardAmount else p0.amount
}
override fun onRewardedAdClosed() {
// 광고를 온전하게 시청후 닫았을때만 콜백함수에 전달을 해준다.
// 해당 처리가 없으면 광고를 다보지도 않고 껐는데도 보상이 주어지는 버그가 발생한다.
if (isRewarded) {
adCallback.onRewardedAndClosed(rewardAmount)
}
// List에서 캐싱되어 있는 광고를 제거하고 새로 로드한다
consumeAndRefresh()
}
}
rewardAd.show(activity, innerAdCallback)
} else {
// 광고 에러 다이얼로그
ErrorDialogGenerator.getInstance(activity).onAdmobNotLoaded(false)
}
}
|
cs |
위 함수는 광고를 보여 주는 로직이다. 사용법은 아래에 기술되어 있다.
PlayRewardAdCallback 인터페이스는 다음과 같이 정의되어 있다.
1
2
3
|
interface PlayRewardAdCallback {
fun onRewardedAndClosed(rewardAmount: Int)
}
|
cs |
Builder 클래스
Builder 패턴을 사용하면 객체를 Singleton 하게 사용할 수 있으며, 인자들을 선택적으로 활용할 수 있다.
해당 객체를 인스턴스화 할때 사용되는 Builder 클래스 이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
class Builder {
private var rewardAdAmount: Int = -1
private var isDeveloperMode = false
private var activity: Activity? = null
private var maxCachedAds: Int = 1
private var adUnit: AdUnit = AdUnit.TEST
fun activity(activity: Activity): Builder {
this.activity = activity
return this
}
fun setDeveloperMode(mode: Boolean): Builder {
this.isDeveloperMode = mode
return this
}
fun rewardAdAmountOnDebug(amount: Int): Builder {
this.rewardAdAmount = amount
return this
}
fun maxCachedAds(maxCachedAds: Int): Builder {
this.maxCachedAds = maxCachedAds
return this
}
fun adUnit(adUnit: AdUnit): Builder {
this.adUnit = adUnit
return this
}
fun build(): PlayRewardAd {
if (activity == null) {
throw IllegalStateException("You must set activity")
}
if (isDeveloperMode && rewardAdAmount == -1) {
throw IllegalStateException("You must set reward amount in developer mode")
}
return PlayRewardAd(activity!!, isDeveloperMode, rewardAdAmount, maxCachedAds, adUnit).apply {
initialize()
}
}
}
|
cs |
사용법
사용하고자 하는 곳에서는 다음과 같이 인스턴스화를 해주자
1
2
3
4
5
6
7
8
9
|
private lateinit var rewardAd: PlayRewardAd
val rewardAd = PlayRewardAd.Builder()
.activity(requireActivity()) // 광고 객체를 위한 context
.setDeveloperMode(BuildConfig.DEBUG) // 개발모드인지 출시모드인지
.rewardAdAmountOnDebug(1) // 개발모드 일때 강제로 보상 갯수를 지정해준다
.maxCachedAds(2) // 캐싱할 광고 수
.adUnit(PlayRewardAd.AdUnit.A) // 광고 단위 아이디
.build()
|
cs |
광고를 보여주고자 하는 시점에는 다음과 같이 사용하면된다.
1
2
3
4
5
6
7
8
9
|
val adCallback: PlayRewardAdCallback = object : PlayRewardAdCallback {
override fun onRewardedAndClosed(rewardAmount: Int) {
//광고를 온전히 시청했음을 알려주는 콜백함수
//이곳에서 광고를 다본후 원하는 작업들을 실행하면 된다
//(ex. 100원 보상 주기 등등)
}
}
rewardedAd.show(adCallback)
|
cs |
역시 사용할때 편리하고 간결해야 코딩하는 맛이 난다.
다음은 코드 전문이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
import android.app.Activity
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.rewarded.RewardItem
import com.google.android.gms.ads.rewarded.RewardedAd
import com.google.android.gms.ads.rewarded.RewardedAdCallback
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback
import java.util.*
class PlayRewardAd private constructor(
private val activity: Activity,
private val isDeveloperMode: Boolean,
private val forcedRewardAmount: Int,
private val enableCaching: Int,
private val adUnit: AdUnit
) {
/**
* List for caching reward ad to see without waiting
* + - - + - - +
* show <- | ad | ad | <- load
* + - - + - - +
*/
private val cachedRewardAd = LinkedList<RewardedAd>()
private fun initialize() {
cachedRewardAd.clear()
for(i in 0 until enableCaching) {
cachedRewardAd.addLast(createAndLoadRewardedAd())
}
}
private fun consumeAndRefresh() {
cachedRewardAd.removeFirst()
cachedRewardAd.addLast(createAndLoadRewardedAd())
}
private fun createAndLoadRewardedAd(): RewardedAd {
val rewardedAd = if (isDeveloperMode) {
// 테스트 광고 단위
RewardedAd(activity, AdUnit.TEST.id)
} else {
//프로덕션 광고 단위
RewardedAd(activity, adUnit.id)
}
val adLoadCallback: RewardedAdLoadCallback = object : RewardedAdLoadCallback() {
override fun onRewardedAdLoaded() {
}
override fun onRewardedAdFailedToLoad(errorCode: Int) {
// Ad failed to load.
}
}
rewardedAd.loadAd(AdRequest.Builder().build(), adLoadCallback)
return rewardedAd
}
fun show(adCallback: PlayRewardAdCallback) {
if (cachedRewardAd.size == 0) {
ErrorDialogGenerator.getInstance(activity).onAdmobNotLoaded(false)
return
}
var isRewarded = false
var rewardAmount = 1
val rewardAd = cachedRewardAd.first
if (rewardAd.isLoaded) {
val innerAdCallback: RewardedAdCallback = object : RewardedAdCallback() {
override fun onUserEarnedReward(p0: RewardItem) {
isRewarded = true
rewardAmount = if (isDeveloperMode) forcedRewardAmount else p0.amount
}
override fun onRewardedAdClosed() {
if (isRewarded) {
adCallback.onRewardedAndClosed(rewardAmount)
}
consumeAndRefresh()
}
}
rewardAd.show(activity, innerAdCallback)
} else {
ErrorDialogGenerator.getInstance(activity).onAdmobNotLoaded(false)
}
}
class Builder {
private var rewardAdAmount: Int = -1
private var isDeveloperMode = false
private var activity: Activity? = null
private var maxCachedAds: Int = 1
private var adUnit: AdUnit = AdUnit.TEST
fun activity(activity: Activity): Builder {
this.activity = activity
return this
}
fun setDeveloperMode(mode: Boolean): Builder {
this.isDeveloperMode = mode
return this
}
fun rewardAdAmountOnDebug(amount: Int): Builder {
this.rewardAdAmount = amount
return this
}
fun maxCachedAds(maxCachedAds: Int): Builder {
this.maxCachedAds = maxCachedAds
return this
}
fun adUnit(adUnit: AdUnit): Builder {
this.adUnit = adUnit
return this
}
fun build(): PlayRewardAd {
if (activity == null) {
throw IllegalStateException("You must set activity")
}
if (isDeveloperMode && rewardAdAmount == -1) {
throw IllegalStateException("You must set reward amount in developer mode")
}
return PlayRewardAd(activity!!, isDeveloperMode, rewardAdAmount, maxCachedAds, adUnit).apply {
initialize()
}
}
}
enum class AdUnit(val id: String) {
TEST("ca-app-pub-3940256099942544/5224354917"),
A("your unit id"),
B("your unit id"),
C("your unit id");
}
}
|
cs |
'Android' 카테고리의 다른 글
RxBus - Rxjava로 전역 callback으로 구현하기 (0) | 2021.12.08 |
---|---|
[Android] Firebase Remote Config 사용하기 (0) | 2021.12.06 |
Activity / Fragment ViewBinding Boilerplate Class (0) | 2021.12.05 |
Google Admob 광고 사용하기 (0) | 2020.05.04 |