最近入职了新公司,开端触摸广告,以前没怎么搞过,看了其他项目的广告老艺人的封装,便是搞一个类,命名为 xx广告Manager,然后什么代码全写里面,好家伙,果然厉害,代码直面实质让人一下就看懂了,果然是老艺人。
……
所以决议自己去封装一下。
看了 github 上相关的项目,只找到 TogetherAd 还有 EasyAds-Android 两个比较好的,看了一下,感觉都很杂乱,各种配置,运用办法也不是我想要的。
现在接的都是融合 sdk,感觉广告不需求再分平台了,然后个人喜欢那种初始化一次,一句话调用的方式,不论内部写得好不好,API 必须给整美丽。所以仍是自己写一个吧。
先上库房地址: EasyAdvApp
现在只封装了自己用到的广告场景,如果有更杂乱的广告场景能够提出一起完善,觉得还行能够支撑一下。
下面只会大约说下封装进程中一些简单的思路,结构的详细运用在 github 上有写。
运用:
EasyAdv.splashConfig()
.setCodeId("KFC_V_WO_50")
.setActivity(this)
.setWidth(getScreenWidth())
.setHeight(getScreenHeight())
.setContainer(findViewById<FrameLayout>(R.id.adLayout))
.setSplashAdvListener(onAdTimeOver = {
toMain()
})
.showSplashAdv()
首要总结下广告封装需求什么功用:
- 尽量和业务逻辑别离,统一 api 调用
- 由于各种广告sdk的回调很多,最后能有一个当地全局回调,能够统一处理埋点之类的需求
- 运用进程中不需求关怀某个广告的详细完结,只管调用就行。
- 其他…(不知道怎么吹了)
设计(以开屏为例):
各种功用的完结应该是基于接口方式的,这样能够将详细功用与结构逻辑别离,易于替换。
interface ISdkPlatform {
fun initAdvSdk(
builder: AdvSDKBuilder,
callback: AdvSdkInitCallback
)
}
首要是广告 sdk 的初始化,完结该接口的 initAdvSdk 办法来初始化详细的sdk,builder 里面存储各种需求的参数,初始化完结经过 callback 回调给结构:
class TTAdSdkPlatform : ISdkPlatform {
override fun initAdvSdk(builder: AdvSDKBuilder, callback: AdvSdkInitCallback) {
TTAdSdk.init(...)
TTAdSdk.start(object : TTAdSdk.Callback {
override fun success() {
callback.onInitSuccess()
}
override fun fail(code: Int, msg: String?) {
callback.onInitFail(code, msg)
}
})
}
}
同理然后各种广告的详细完结也设计成对应的接口:
interface ISplashAdvEngine {
fun showSplashAdv(config: SplashAdvConfig)
}
interface IBannerAdvEngine {
fun showBannerAdv(config: BannerAdvConfig)
}
...
然后在结构初始化时设置进去就行
EasyAdv.init(this)
.setPlatform(TTAdSdkPlatform())
.setSplashAdvEngine(TTSplashAdvEngine())
在运用结构功用时,就再也不必管广告的完结了,由于初始化的时分已经设置好了。
一开端说到,咱们需求一些全局性的东西,比方一些公共参数,回调监听等,好处是设置了一次就能用,不需求每次都调用一次,比方埋点等功用。
那能够用一个实体类来存储这些参数,能够经过builder等方式去构建:
class GlobalAdvConfig {
var splashAdvListener: SplashAdvListener? = null
var userId: String = ""
}
其实 builder 形式挺麻烦,要写各种 set,最后还要 build 一下,这儿能够用 DSL 去优化一下:
fun sdkConfig(options: AdvSDKBuilder.() -> Unit) = apply {
advSDKBuilder = AdvSDKBuilder().also { options(it) }
}
EasyAdv.init(this)
.setGlobalAdvConfig {
userId = "KFC_V_WO_50"
splashAdvListener = xxx()
}
能够看到会简洁和便利很多。
结构初始化的东西差不多是这些,其他都同理差不多,下面讲讲开屏广告的问题
开屏广告分为冷启动和热启动
先说冷启动,遇到的问题其实主要是冷启动其实挺快的,由于sdk初始化要在赞同隐私合规之后才干进行,但是广告的初始化进程是异步的,往往你调了开屏广告办法,sdk还没初始化完结的状况。。
由于结构的设置是初始化和运用分隔的,所以调用冷启动广告时,要判别 sdk 是否初始化完结,没完结的时分要等候完结才去履行,然后还要有超时逻辑,超越一定时间没反应就直接进主页了。
如何完结这个等候进程,一开端想着用粘性广播,貌似有点杂乱,然后想着 looper ,感觉用 while 循环貌似可行。
上面广告 sdk 初始化接口中,咱们能够在 callback 拿到结果,那么能够用个变量符号下成功与否:
sdkPlatform?.initAdvSdk(builder, object : AdvSdkInitCallback {
override fun onInitSuccess() {
isFinishSdkInit = true
isInitSdkSuccess = true
}
override fun onInitFail(code: Int, msg: String?) {
isFinishSdkInit = true
isInitSdkSuccess = false
}
})
一个代表是否完结初始化,一个代表是否初始化成功。
那么在 showSplashAdv 的时分,就能够经过这两个变量来进行等候逻辑了:
private var splashAdvFlag = false
fun showSplashAdv(config: SplashAdvConfig, splashAdvEngine: ISplashAdvEngine?) {
showSplashAdvTime = System.currentTimeMillis()
//等候sdk初始化完结
while (!splashAdvFlag) {
if (EasyAdv.isFinishSdkInit) {
splashAdvFlag = if (EasyAdv.isInitSdkSuccess) {
splashAdvEngine?.showSplashAdv(config)
true
} else {
config.listener(CallbackType.ERROR, EasyAdv.ERROR_SDK, "sdk init fail")
true
}
} else {
if (EasyAdv.sdkTimeOutMillis > 0) {
if (System.currentTimeMillis() - showSplashAdvTime > EasyAdv.sdkTimeOutMillis) {
config.listener(CallbackType.ERROR, EasyAdv.ERROR_SDK, "sdk init time out")
splashAdvFlag = true
}
}
}
}
}
用 splashAdvFlag 来标识是否完毕循环,然后经过判别两个变量来进行等候逻辑,只要都为 true 的时分履行详细的开屏办法,不然回调失败或者超时等逻辑。
热启动开屏
热启动其实便是监听 ActivityLifecycleCallbacks 然后再对应的弹出广告。这儿我想的是,我只把判别是否是热启动的逻辑封装到结构中,详细广告逻辑仍是放开给用户。
所以仍是接口整一个:
interface IHotSplashAdvStrategy {
fun getCodeId(): String
fun showRequirement(activity: Activity): Boolean
fun showHotSplashAdv(activity: Activity, codeId: String)
}
showRequirement 是显现条件,比方我要10分钟显现一次,或者哪个页面不显现等逻辑能够写里面,showHotSplashAdv 便是详细的显现逻辑了。其实常规做法便是弹个全屏 Dialog,里面调 开屏广告那一套即可。
就这样吧,写得废物,不写了。。。