背景
随着移动流量的增长,各种 App 上的广告也涌现而出,几乎每一个热门的 App 都会有开屏广告。简单来说,开屏广告就是 App 第一次启动打开时立刻就出现的广告页面,业内通常以曝光或点击等方式来计量收费。
为什么会有开屏广告?
每个 App 的打开过程都会有一段空白时间,由于程序响应或手机性能、网速等因素,很少有 App 能够直接秒开的。而这段空白时间用来展示开屏广告,正好能填补这段空白,同时还有利于用户体验的提升。
另一个重要因素,对于商业化的 App 来说,开屏广告也是产品盈利的一个重要手段,开屏广告位的报价也是最高的。
开屏广告的特点
-
广告位优质
只要用户使用 App,每次启动 App 时都会看到开屏广告,相比站内广告而言,展现量会高出很多倍,且更靠前,提前抓取意向客户。
-
强制曝光
启动 App 后会默认展示,强制实现广告的曝光效果。
-
流量大
对于日活跃度很高的 App ,如:快手等,如果每个用户都看到你的广告,可想而知带来的流量是巨大的。
-
针对性投放
开屏广告可以针对用户的特点针对性投放,通过平台的用户画像,可针对用户端性别、年龄、兴趣等进行针对性投放。
框架设计
初次接到需求时,第一直觉会感到十分简单,无非是图片的展示或播放视频。但随着业务的不断迭代,更多新的产品逻辑的加入,让你看似简单的开屏广告,却不再简单。因此,设计一个高可用、无需要频繁发版的开屏广告框架,是十分有必要的。
如下图,以 App 的生命周期为节点,来实现广告的展示和相关逻辑的触发。
-
首次冷启动
在冷启动结束之后,也就是 didFinish 阶段触发数据的更新和广告的展示。
数据更新包括:请求广告的数据、更新本地缓存、后台下载广告资源(图片或视频)等。
-
展示开屏广告
广告展示的逻辑是:检查缓存中是否有广告数据、获取一个有效的广告进行渲染(所谓有效的广告是指:本地有缓存资源、在有效期内等有效条件)。
-
进后台
当 App 进入后台时,也会触发数据更新的逻辑,目的是为了获取最新需要展示的广告数据,在下次启动(冷启或热启)时,再次进行展示。
-
进前台
不要只在冷启动 didFinish 时才展示广告,有可能用户经常使用该 App 不会轻易 kill ,只是按了 Home 键或者上推进入了后台,此时 App 仍然存活,再次启动不会触发 didFinish 。 因此需要在 App 返回前台的时候再次出发冷启动时候的相关逻辑。
广告类型
开屏广告展示的资源具有多样性,在设计框架的时候就要考虑到多种格式资源的适配,这会直接影响框架的架构选择,常见的资源格式如:
如上图,分别有普通图片、gif 动图、视频等格式。
缓存策略
当网络非常好的情况下,直接去加载网络图片或者视屏可能不会有影响。但任何用户都可能有网络不佳的情况,如果启动后展示的开屏广告页面一直处于白屏或者 loading 态,甚至倒计时结束资源还未显示完全,不仅是严重影响了用户体验,同时对开屏广告的效果(推送、宣传、盈利等)也大打折扣。因此,需要采取一些策略来保证开屏广告的质量,如:
- 资源缓存
- 首次启动时,先拉取开屏广告的配置数据,本地缓存;然后对每个广告的资源进行后台下载,并缓存到指定目录;
- 下次启动(冷启或热启)时,按照上述展示逻辑过滤出符合要求的、已缓存的广告数据进行展示。
- 资源刷新
支持数据的刷新操作,及时资源已经缓存到本地,如果后台配置的资源更新了,需要删除本地资源,重新下载该广告的资源。
综上,整个数据的加载和缓存过程,如下图:
展示逻辑
首先,我们先看下启动后检测是否可以展示的过程,这里会有多个条件的判断: 1、首先检查本地缓存中是否存在有效的广告数据
2、再检查有效的广告数据对应的资源是否已经缓存到本地
3、若存在同时满足了以上两个条件的广告,则按优先级获取第一个,上屏渲染。
如下图:
对于开屏广告的展示的条件也会有多种场景的限制,所以为了保证开品广告的曝光量,也要合理的规划广告展现的次数和时机,通常需要考虑的因素有:
-
多个广告的优先级
对于热门 App 来说会有多个开屏广告需要曝光,在 server 配置后,端上也会有多个广告数据的缓存,为满足不同广告的优先级,可根据广告的优先级进行缓存和曝光。
-
展示次数
同一个广告的曝光次数也是有上限的,此时需要注意广告曝光后前后端的数据同步以及本地缓存的刷新等。
-
最短间隔(or 每次启动 or 前后台切换)
上面的流程图中我们看到 App 再次进入前台还会检查是否要展示开屏,如果每次打开 App 都展示开屏广告,无疑会对用户的体验造成影响。这里要控制好两次开屏广告的最小时间间隔,避免过度打扰用户。
-
有效期
开屏广告当然是存在有效期限的,也是每次展示之前判断的一个必要条件。
-
紧急下架
这里还要考虑一种特殊的情况,本地缓存的广告数据在有限期,由于某种特殊情况,需要紧急下架某个广告,为了让前端尽早更新缓存,也可考虑长连接或者推送机制等手段来实现。
-
其他条件等
侵入性
对于接入开屏需求的项目来说,代码的耦合性越弱,对项目管理越简单。因此以组件化的方式开发开屏广告框架是必要的,可以通过在框架内部监听 App 启动相关变化的通知,来完成开屏的展示和缓存等逻辑。主要是三个阶段:didFinish 、 enterBackground 、 enterForeground 。
objectivec
///在load 方法中,设置 App 生命周期的监听,可以做到无侵入
+ (void)load
{
[self shareInstance];
}
- (instancetype)init
{
self = [super init];
if (self) {
///在此不能做任何耗时的事情,防止影响启动速度
///应用启动, 首次开屏广告
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
///要等DidFinished方法结束后才能初始化UIWindow,不然会检测是否有rootViewController
* 更新数据
* 检测是否展示广告
}];
///进入后台
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
* 更新数据
}];
///后台启动,二次开屏广告
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
* 检测是否展示广告
}];
}
return self;
}
上屏
对于广告的上屏展示,可能最先想到的是直接在 keyWindow 上添加广告视图,无非就是展示在最上层,随着项目复杂度的提升,向 keyWindow 添加的视图会增加,代码的耦合性就显现出来了。同时也可能会被业务的 alert 覆盖(抢占 keyWindow)。
为了提升框架代码的内聚性,同时满足开屏广告视图在最顶层,不会被其他业务逻辑覆盖,可以初始化一个新的 UIWindow 用来呈现广告视图,
ini
- (void)showAd {
///初始化一个Window, 做到对业务视图无干扰。
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
///设置广告VC
window.rootViewController = [ADVC new];
///提高window 层级,防止 AlertView 等弹窗的覆盖
window.windowLevel = UIWindowLevelStatusBar + 1;
///默认为YES,当你设置为NO时,这个Window就会显示了
window.hidden = NO;
///来个渐显动画
window.alpha = 0;
[UIView animateWithDuration:0.3 animations:^{
window.alpha = 1;
}];
window.makeKeyAndVisible()
///防止释放,显示完后要手动设置为 nil
self.window = window;
}
小结
对与商业化的项目来说,通过开屏广告实现营收是运营中的一个重要策略。 对于那些还未商业化的项目来说,但还是有很多场景可以通过开屏广告的形式向用户传递一些活动信息,如:员工生日祝福、周年庆等等。
最后,作为开发者,在方案设定的过程中,也要考虑设置合理广告时长和频次等条件的设定,避免过度打扰用户。