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

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

相关推荐
paid槮1 小时前
MySql基础:数据类型
android·mysql·adb
用户2018792831673 小时前
AMS和app通信的小秘密
android
用户2018792831673 小时前
ThreadPoolExecutor之市场雇工的故事
android
诺诺Okami3 小时前
Android Framework-Launcher-InvariantDeviceProfile
android
Antonio9154 小时前
【音视频】Android NDK 与.so库适配
android·音视频
sun00770013 小时前
android ndk编译valgrind
android
AI视觉网奇14 小时前
android studio 断点无效
android·ide·android studio
jiaxi的天空14 小时前
android studio gradle 访问不了
android·ide·android studio
No Silver Bullet15 小时前
android组包时会把从maven私服获取的包下载到本地吗
android
catchadmin15 小时前
PHP serialize 序列化完全指南
android·开发语言·php