本文首发于我的博客:http://t.cn/RijitdD
最初接下开屏广告热启动需求时,对于即将踏入一个什么样的深坑,我心里毫无概念。在当时看来,开屏广告的相关代码已经基本实现,我只要额外添加热启动功能就可以,即使算上调研设计、后端联调加上测试的时间,我也只给自己规划了一周多的时间来完成双端的需求。
需求
所谓的开屏广告热启动是指,应用程序进入后台后(按 Home 键或者跳转到其他应用),等待一段时间再回到应用时展示开屏广告。由于操作系统会定时清理不活跃且占内存的应用,所以此时展示开屏广告会让用户以为应用正在重新启动。由于对用户体验伤害小,甚至很多时候几乎可以做到无感知,所以目前很多日活量较高的 app 都实现了开屏广告热启动功能,常见的有微博、头条等。
如果不考虑最短间隔时间,每天热启动次数上限等附加限制,开屏广告热启动的核心需求其实就在于准确地检测应用切到后台再回到前台的行为。所谓的准确,指的是不漏掉真正的进入后台,也不误把普通操作当做进入后台。
这个需求看上去非常容易,直接调用系统 API 即可完成,而在实际开发的过程中却遇到了不少坑,我按照平台逐一分析一下,不会有太多的实现细节,主要是聊聊设计和实现思路。
iOS
先说我最熟悉的,也是相对来说比较容易实现的 iOS 平台。
在实现开屏广告需求时,从设计角度来考虑,由于 application:didFinishLaunchingWithOptions:
函数执行结束后会自动发送通知,所以我们只需要监听 UIApplicationDidFinishLaunchingNotification
通知即可。在展示广告时,可以使用 UIView
直接盖上一张图片。不过考虑到有倒计时按钮,跳过按钮,以及将来有可能支持除了图片以外的其他格式(比如 VR 视频),所以使用 UIViewController
虽然麻烦些,但也不失为一种稳妥的,方便后续拓展维护的做法。
具体做法就不详细描述了,感兴趣的读者可以参考 无入侵的开屏广告插入方式。
前文说过热启动需要满足一定条件,比如进入后台和再次回到前台的时间间隔必须大于某个值,否则回到桌面后快速返回应用也会出现开屏广告,带给用户的体验很差。并且这个值最好是做成服务器动态下发,好处是一旦开屏广告的逻辑出现问题,可以把间隔时间设为非常大的值,从而关闭此功能。同样是出于用户体验考虑,每天开屏广告热启动的次数也需要做限制,超出预设次数以后不再展示。
为了管理以上逻辑,并且与原有开屏广告逻辑有效解耦,单独抽离一个 HotSplashManager
类就显得很有必要。由于应用的整个生命周期内都有可能展示开屏广告,所以可以考虑设计为单例模式,并且对外统一暴露一个 - (BOOL)canShowHotSplashAdvertisement
方法。
不过由于目前项目中没有使用通知,而是与 application:didFinishLaunchingWithOptions:
方法强耦合。所以我接手以后的思路也是沿用前人的代码,主要是在 applicationDidEnterBackground
函数中通知 HotSplashManager
类应用进入后台。
锁屏检测
这里的第一个小坑在于锁屏同样会触发 applicationDidEnterBackground
函数,而从逻辑上讲,应用锁屏后再解锁并不应该被认为是一种前后台切换,而如果已经按 Home 键进入后台,这时候再锁屏/解锁就不应该影响 App 进入了后台再切回前台的事实,也就是不影响开屏广告的正常展示,这里的逻辑比较绕,需要整理一下逻辑并仔细测试。
检测锁屏和解锁的方法有好几种, 其中有的方法不能完全兼容 iOS 9、10 两大主流版本。最终找到的有效方案是利用 Darwin 层面的通知:
// 检测锁屏和解锁
CFNotificationCenterAddObserver(CFNotificationCe