穿山甲-融合广告SDK的封装和使用

最近入职了新公司,开始接触广告,以前没怎么搞过,看了其他项目的广告老演员的封装,就是搞一个类,命名为 xx广告Manager,然后什么代码全写里面,好家伙,果然厉害,代码直面本质让人一下就看懂了,果然是老演员。

......

所以决定自己去封装一下。

看了 github 上相关的项目,只找到 TogetherAd 还有 EasyAds-Android 两个比较好的,看了一下,感觉都很复杂,各种配置,使用方法也不是我想要的。

现在接的都是融合 sdk,感觉广告不需要再分平台了,然后个人喜欢那种初始化一次,一句话调用的形式,不管内部写得好不好,API 必须给整漂亮。所以还是自己写一个吧。

先上仓库地址: EasyAdvApp

目前只封装了本人用到的广告场景,如果有更复杂的广告场景可以提出共同完善,觉得还行可以支持一下。

下面只会大概说下封装过程中一些简单的思路,框架的具体使用在 github 上有写。

使用:

kotlin 复制代码
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. 其他...(不知道怎么吹了)

设计(以开屏为例)

各种功能的实现应该是基于接口形式的,这样可以将具体功能与框架逻辑分离,易于替换。

kotlin 复制代码
interface ISdkPlatform {
    fun initAdvSdk(
        builder: AdvSDKBuilder,
        callback: AdvSdkInitCallback
    )
}

首先是广告 sdk 的初始化,实现该接口的 initAdvSdk 方法来初始化具体的sdk,builder 里面存储各种需要的参数,初始化完成通过 callback 回调给框架:

kotlin 复制代码
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)
            }
        })
    }
}

同理然后各种广告的具体实现也设计成对应的接口:

kotlin 复制代码
interface ISplashAdvEngine {
    fun showSplashAdv(config: SplashAdvConfig)
}

interface IBannerAdvEngine {
    fun showBannerAdv(config: BannerAdvConfig)
}
...

然后在框架初始化时设置进去就行

kotlin 复制代码
 EasyAdv.init(this)
        .setPlatform(TTAdSdkPlatform())
        .setSplashAdvEngine(TTSplashAdvEngine())

在使用框架功能时,就再也不用管广告的实现了,因为初始化的时候已经设置好了。

一开始说到,我们需要一些全局性的东西,比如一些公共参数,回调监听等,好处是设置了一次就能用,不需要每次都调用一次,比如埋点等功能。

那可以用一个实体类来存储这些参数,可以通过builder等方式去构建:

javascript 复制代码
class GlobalAdvConfig {
    var splashAdvListener: SplashAdvListener? = null
    var userId: String = "" 
}

其实 builder 模式挺麻烦,要写各种 set,最后还要 build 一下,这里可以用 DSL 去优化一下:

kotlin 复制代码
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 拿到结果,那么可以用个变量标记下成功与否:

kotlin 复制代码
sdkPlatform?.initAdvSdk(builder, object : AdvSdkInitCallback {
    override fun onInitSuccess() {
        isFinishSdkInit = true
        isInitSdkSuccess = true
    }

    override fun onInitFail(code: Int, msg: String?) {
        isFinishSdkInit = true
        isInitSdkSuccess = false
    }
})

一个代表是否完成初始化,一个代表是否初始化成功。

那么在 showSplashAdv 的时候,就可以通过这两个变量来进行等待逻辑了:

kotlin 复制代码
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 然后再对应的弹出广告。这里我想的是,我只把判断是否是热启动的逻辑封装到框架中,具体广告逻辑还是放开给用户。

所以还是接口整一个:

kotlin 复制代码
interface IHotSplashAdvStrategy {
    fun getCodeId(): String

    fun showRequirement(activity: Activity): Boolean

    fun showHotSplashAdv(activity: Activity, codeId: String)
}

showRequirement 是显示条件,比如我要10分钟显示一次,或者哪个页面不显示等逻辑可以写里面,showHotSplashAdv 就是具体的显示逻辑了。其实常规做法就是弹个全屏 Dialog,里面调 开屏广告那一套即可。

就这样吧,写得垃圾,不写了。。。

相关推荐
图王大胜25 分钟前
Android SystemUI组件(11)SystemUIVisibility解读
android·framework·systemui·visibility
服装学院的IT男4 小时前
【Android 13源码分析】Activity生命周期之onCreate,onStart,onResume-2
android
Arms2064 小时前
android 全面屏最底部栏沉浸式
android
服装学院的IT男5 小时前
【Android 源码分析】Activity生命周期之onStop-1
android
ChinaDragonDreamer7 小时前
Kotlin:2.0.20 的新特性
android·开发语言·kotlin
网络研究院9 小时前
Android 安卓内存安全漏洞数量大幅下降的原因
android·安全·编程·安卓·内存·漏洞·技术
凉亭下9 小时前
android navigation 用法详细使用
android
小比卡丘12 小时前
C语言进阶版第17课—自定义类型:联合和枚举
android·java·c语言
前行的小黑炭13 小时前
一篇搞定Android 实现扫码支付:如何对接海外的第三方支付;项目中的真实经验分享;如何高效对接,高效开发
android
落落落sss14 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis