最近入职了新公司,开端触摸广告,以前没怎么搞过,看了其他项目的广告老艺人的封装,便是搞一个类,命名为 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()

首要总结下广告封装需求什么功用:

  1. 尽量和业务逻辑别离,统一 api 调用
  2. 由于各种广告sdk的回调很多,最后能有一个当地全局回调,能够统一处理埋点之类的需求
  3. 运用进程中不需求关怀某个广告的详细完结,只管调用就行。
  4. 其他…(不知道怎么吹了)

设计(以开屏为例)

各种功用的完结应该是基于接口方式的,这样能够将详细功用与结构逻辑别离,易于替换。

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,里面调 开屏广告那一套即可。

就这样吧,写得废物,不写了。。。