iOS全埋点解决方案-应用退出和启动
前言
通过应用程序退出事件,可以分析应用程序的平均使用时长;通过应用程序的启动事件,可以分析日活和新增。我们可以通过全埋点方式 SDK 实现应用程序的退出和启动事件。
一、全埋点的简介
目前、全埋点采集可以采集一下4个事件。
1、$AppEnd 事件:应用程序退出事件
2、$AppStart 事件:应用程序启动事件
3、$AppViewScreen 事件: 应用程序内界面预览事件,对于 iOS 来说就是切换不同的 UIViewController。
4、$AppClick 事件: 控件的点击事件,比如点击 UIButton 、UITableView 等。
预置事件:在 SDK 中自动采集的事件称为预置事件。
二、应用程序退出
2.1 应用程序状态:
一个标准的 iOS 程序在不同的时期会有不同的运行状态,在 iOS 程序中常见的状态有5中。如图所示:
1、Not running:非运行状态,指应用程序还没有被启动,或者已经被系统终止。
2、Inactive: 前台非活跃状态,指应用程序即将进入前台状态。
3、Active: 前台活跃状态,指应用程序正在前台运行,可接受事件并进行处理。
4、Background: 进入后台状态,指应用程序进入后台并可执行代码。
5、Suspended: 挂起状态,指应用程序进入后台并没有执行代码,系统会自动将应用程序转移到该状态。挂起时,应用程序会保留在内存中,但不执行任何代码,当系统出现内存不足情况时,系统会清除被挂起的应用程序。
在应用程序的状态转换过程中,系统会调用实现 UIApplicationDelegate 协议类的一些方法,并发送相应的本地通知(先调用方法,待回调方法执行后,再发相应的通知),回调方法和本地通知的对应关系如下表
回调方法 | 本地通知 |
---|---|
- application:didFinishI aunchingWithOptions: | UIApplicationDidFinishLaunchingNotification |
- applicationDidBecomeActive: | UIApplicationDidBecomeActiveNotification |
- applicationWillResignActive: | UIApplicationWillResignActiveNotification |
- applicationDidEnterBack ground:· | UIApplicationDidEnterBackgroundNotification |
- applicationWillEnterForeground: | UIApplicationWillEnterForegroundNotificatio |
- applicationWillTerminate: | UIApplicationWillTerminateNotification |
2.2 实现步骤
通过上面介绍的内容可知,当一个 iOS 应用程序退出时,就意味着该应用程序进入了“后台”,即处于 Background 状态。因此,对于实现 $AppEnd 事件的全埋点,我们只需要注册监听 UIApplicationDidEnterBackgroundNotification 通知,然后在收到通知时触发 $AppEnd 事件,即可达到 $AppEnd 事件全埋点的效果。
第一步:注册监听 UIApplicationDidEnterBackgroundNotification 本地通知。
在 SensorsAnalyticsSDK.m 文件中实现 - setupListeners 方法,用来监听 UIApplicationDidEnterBackgroundNotification 本地通知,然后再相应的回调方法中触发 $AppEnd 事件。
- (void)setupListeners {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
// 注册监听 UIApplicationDidEnterBackgroundNotification 本地通知
// 当应用程序进入后台,调用通知方法
[center addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}
- (void)applicationDidEnterBackground:(NSNotification *)notification {
NSLog(@"Application did enter background.");
// 触发 AppEnd 事件
[self track:@"$AppEnd" properties:nil];
}
第二步:在 SensorsAnalyticsSDK.m 文件中初始化 - init 方法中调用 - setupListeners,并在 - dealloc 方法中移除监听。
- (instancetype)init {
self = [super init];
if (self) {
_automaticProperties = [self collectAutomaticProperties];
// 添加应用程序状态监听
[self setupListeners];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
第三步:测试验证
我们可以在 Xcode 中打印控制台中查看如下的打印信息。
{
"event" : "$AppEnd",
"time" : 1648520301691,
"propeerties" : {
"$model" : "x86_64",
"$manufacturer" : "Apple",
"$lib_version" : "1.0.0",
"$os" : "iOS",
"$app_version" : "1.0",
"$os_version" : "15.2",
"$lib" : "iOS"
}
}
三、应用程序启动
应用程序的启动,一般情况下,大致可以分为两类场景:
• 冷启动
• 热启动(从后台恢复)
不管是冷启动还是热启动,触发 $AppStart 事件的时机,都可以理解成是当“应用程序开始进入前台并处于活动状态”,也即前文介绍的 Active 状态。因此,为了实现 $AppStart 事件的全埋点,我们可以注册监听 UIApplicationDidBecomeActiveNotification 本地通知,然后在其相应的回调方法里触发 $AppStart 事件。
3.1 实现步骤
第一步:在 SensorsAnalyticsSDK.m 文件 - setupListeners 方法中,添加 UIApplicationDidBecomeActiveNotification 本地通知,然后再相应的回调方法中触发 $AppStart 事件。
- (void)setupListeners {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
// 注册监听 UIApplicationDidBecomeActiveNotification 本地通知
// 当应用程序进入前台台,调用通知方法
[center addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
NSLog(@"Application did enter active.");
// 触发 AppEnd 事件
[self track:@"$AppStart" properties:nil];
}
第二步: 测试验证
可以在 Xcode 打印控制台中查看下面的打印信息。
{
"event" : "$AppStart",
"time" : 1648520708355,
"propeerties" : {
"$model" : "x86_64",
"$manufacturer" : "Apple",
"$lib_version" : "1.0.0",
"$os" : "iOS",
"$app_version" : "1.0",
"$os_version" : "15.2",
"$lib" : "iOS"
}
}
3.2 优化
问题:
通过测试可以发现,仍有以下几个特殊场景存在问题:
• 下拉通知栏并上滑,会触发 $AppStart 事件
• 上滑控制中心并下拉,会触发 $AppStart 事件
• 双击 Home 键进入切换应用程序页面,最后又选择当前应用程序,会触发 $AppStart 事件
以上几个场景均会触发 $AppStart 事件,明显与实际情况有所不符。
那这些现象是什么原因导致的呢?
我们继续分析可以发现以下几个现象:
• 下拉通知栏时,系统会发送 UIApplicationWillResignActiveNotification 通知;上滑通知栏时,系统会发送 UIApplicationDidBecomeActiveNotification 通知
• 上滑控制中心时,系统会发送 UIApplicationWillResignActiveNotification 通知;下拉控制中心时,系统会发送 UIApplicationDidBecomeActiveNotification 通知
• 双击 Home 键进入切换应用程序页面时,系统会发送 UIApplicationWillResignActiveNotification 通知,然后选择当前应用程序,系统会再发送 UIApplicationDidBecomeActiveNotification 通知
很容易总结出规律:在以上几个场景下,系统均是先发送UIApplicationWillResignActiveNotification 通知,然后再发送 UIApplicationDidBecomeActiveNotification 通知。而我们又是通过注册监听 UIApplicationDidBecomeActiveNotification 通知来实现 $AppStart 事件全埋点,因此均会触发 $AppStart 事件。
那如何解决这个问题呢?
在解决这个问题之前,我们先看另一个现象:不管是冷启动还是热启动,系统均没有发送 UIApplicationWillResignActiveNotification 通知。
因此,只要在收到 UIApplicationDidBecomeActiveNotification 通知时,判断之前是否收到过 UIApplicationWillResignActiveNotification 通知,若没有收到,则触发 $AppStart 事件;若已收到,则不触发 $AppStart 事件。这样即可解决上面的问题。
优化方案:
第一步:在 SensorsAnalyticsSDK.m 文件中添加 applicationWillResignActive 标记位。
/// 标记应用程序是否收到 UIApplicationWillResignActiveNotification 本地通知
@property (nonatomic, assign) BOOL applicationWillResignActive;
第二步:在 - setupListeners 方法中新增注册监听 UIApplicationWillResignActiveNotification 的本地通知。
- (void)setupListeners {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
// 注册监听 UIApplicationDidEnterBackgroundNotification 本地通知
// 当应用程序进入后台,调用通知方法
[center addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
// 注册监听 UIApplicationDidBecomeActiveNotification 本地通知
// 当应用程序进入前台台,调用通知方法
[center addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
// 注册监听 UIApplicationWillResignActiveNotification 本地通知
// 当应用程序进入前台台,调用通知方法
[center addObserver:self
selector:@selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:nil];
}
- (void)applicationWillResignActive:(NSNotification *)notification {
// 设置标记位
self.applicationWillResignActive = YES;
}
第三步:在UIApplicationDidBecomeActiveNotification 的回调方法中还原 applicationWillResignActive 的标记位
- (void)applicationDidBecomeActive:(NSNotification *)notification {
NSLog(@"Application did enter active.");
// 还原标记位
if (self.applicationWillResignActive) {
self.applicationWillResignActive = NO;
return;
}
// 触发 AppStart 事件
[self track:@"$AppStart" properties:nil];
}
第四步:在 UIApplicationDidEnterBackgroundNotification 回调方法中还原 applicationWillResignActive 的标记位
- (void)applicationDidEnterBackground:(NSNotification *)notification {
NSLog(@"Application did enter background.");
// 还原标记位
self.applicationWillResignActive = NO;
// 触发 AppEnd 事件
[self track:@"$AppEnd" properties:nil];
}
第五步:测试验证
{
"event" : "$AppStart",
"time" : 1648533646735,
"propeerties" : {
"$model" : "x86_64",
"$manufacturer" : "Apple",
"$lib_version" : "1.0.0",
"$os" : "iOS",
"$app_version" : "1.0",
"$os_version" : "15.2",
"$lib" : "iOS"
}
}
四、应用程序被动启动
被动启动:我们把由 iOS 系统触发的应用程序自动进入后台运行的启动称之为(应用程序的)被动启动,使用 $AppStartPassively 事件来表示。
4.1、Background modes
使用 Xcode 创建新的应用程序,默认情况下后台刷新功能是关闭的,我们可以在 Capabilities 标签中开启 Background Modes,然后就可以勾选所需要的功能了,如下图所示:
通过上图可知,有如下几种后台运行模式,它们都会触发被动启动($AppStartPassively 事件)。
1、Audio,AirPlay,and Picture in Picture : 音频的播放,录音,AirPlay及画中画的视频播放
2、Location updates:此模式下,会由于地理位置变化而触发应用程序启动
3、Voice over IP : IP网络电话,通过对语音信号进行编码数字化,然后转换成IP数据包在TCP/IP网络上进行传输,从而达到在网络上进行语音通信的目的
4、External Accessory communication:此模式下,一些 MFi 外设通过蓝牙或者 Lightning 接头等方式与 iOS 设备连接,从而可在外设给应用程序发送消息时,触发对应的应用程序启动
5、Uses Bluetooth LE accessories:此模式与 External Accessory communication 类似,只是无需限制 MFi 外设,而需要的是 Bluetooth LE 设备
6、Acts as a Bluetooth LE accessory:此模式下,iPhone 作为一个蓝牙外设连接,可以触发应用程序启动
7、Background fetch:此模式下,iOS 系统会在一定的时间间隔内触发应用程序启动,去获取应用程序数据
8、Remote notifications:此模式是支持静默推送,当应用程序收到这种推送后,不会有任何界面提示,但会触发应用程序启动
9、Background processing: 后端处理
4.2 实现步骤
后台用程序刷新拉起应用程序后,首先会回调 AppDelegate 中的 -application:didFinishLaunchingWithOptions: 方法。因此,我们可以通过注册监听 UIApplicationgDidFinishLaunchingNotification 本地通知来采集被动启动事件信息。
第一步:在 - setupListeners 方法中添加 UIApplicationgDidFinishLaunchingNotification 本地通知,在回调方法中上报数据。
- (void)setupListeners {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
// 注册监听 UIApplicationDidFinishLaunchingNotification 本地通知
// 当应用程序被动,调用通知方法
[center addObserver:self
selector:@selector(applicationDidFinishLaunching:)
name:UIApplicationDidFinishLaunchingNotification
object:nil];
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
// 触发 AppStartPassively 事件
[self track:@"$AppStartPassively" properties:nil];
}
第二步:新增一个私有属性 launchedPassively,标记应用程序是否处于被动启动
/// 标记应用程序是否是被动启动
@property (nonatomic, assign, getter=isLaunchedPassively) BOOL launchedPassively;
第三步:在 - init 初始化方法中,通过 backgroundTimeRemaining 属性是否等于 UIApplicationBackgroundFetchIntervalNever 来设置
- (instancetype)init {
self = [super init];
if (self) {
_automaticProperties = [self collectAutomaticProperties];
// 设置是否需是被动启动标记
_launchedPassively = UIApplication.sharedApplication.backgroundTimeRemaining != UIApplicationBackgroundFetchIntervalNever;
// 添加应用程序状态监听
[self setupListeners];
}
return self;
}
第四步:在 - applicationDidFinishLaunching 回调方法中,如果 isLaunchedPassively 为 YES,再触发 $AppStartPassively 事件
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
NSLog(@"Application did finish launching.");
// 当应用程序后台运行时,触发被动启动事件
if (self.isLaunchedPassively) {
// 触发 AppStartPassively 事件
[self track:@"$AppStartPassively" properties:nil];
}
}
第五步:测试验证
1、开启 Background modes 中的 Background fetch 复选框
2、选择 Demo Scheme , 一次单击 Xcode 菜单栏中的 Product -> Scheme -> Edit -> Scheme -> Run -> Options
3、勾选 Background Fetch 选项,然后点击 Close 按钮。运行 Demo
{
"event" : "$AppStartPassively",
"time" : 1648537321216,
"propeerties" : {
"$model" : "x86_64",
"$manufacturer" : "Apple",
"$lib_version" : "1.0.0",
"$os" : "iOS",
"$app_version" : "1.0",
"$os_version" : "15.2",
"$lib" : "iOS"
}
iOS全埋点解决方案-应用退出和启动的更多相关文章
- iOS全埋点解决方案-UITableView和UICollectionView点击事件
前言 在 $AppClick 事件采集中,还有两个比较特殊的控件: UITableView •UICollectionView 这两个控件的点击事件,一般指的是点击 UITableViewCell 和 ...
- iOS全埋点解决方案-界面预览事件
前言 我们先了解 UIViewController 生命周期相关的内容和 iOS 的"黑魔法" Method Swizzling.然后再了解页面浏览事件($AppViewScr ...
- iOS全埋点解决方案-控件点击事件
前言 我们主要介绍如何实现控件点击事件($AppClick)的全埋点.在介绍如何实现之前,我们需要先了解一下,在 UIKit 框架下,处理点击或拖动事件的 Target-Action 设计模式. ...
- iOS全埋点解决方案-手势采集
前言 随着科技以及业务的发展,手势的应用也越来越普及,因此对于数据采集,我们要考虑如果通过全埋点来实现手势的采集. 一.手势识别器 苹果为了降低开发者在手势事件处理方面的开发难度,定义了一个抽 ...
- iOS全埋点解决方案-时间相关
前言 我们使用"事件模型( Event 模型)"来描述用户的各种行为,事件模型包括事件( Event )和用户( User )两个核心实体.我们在描述用户行为时,往往只需要描述 ...
- iOS全埋点解决方案-采集奔溃
前言 采集应用程序奔溃信息,主要分为以下两种场景: NSException 异常 Unix 信号异常 一.NSException 异常 NSException 异常是 Objectiv ...
- iOS全埋点解决方案-数据存储
前言 SDK 需要把事件数据缓冲到本地,待符合一定策略再去同步数据. 一.数据存储策略 在 iOS 应用程序中,从 "数据缓冲在哪里" 这个纬度看,缓冲一般分两种类型. 内 ...
- iOS全埋点解决方案-APP和H5打通
前言 所谓的 APP 和 H5 打通,是指 H5 集成 JavaScript 数据采集 SDK 后,H5 触发的事件不直接同步给服务器,而是先发给 APP 端的数据采集 SDK,经过 APP 端数 ...
- iOS 全屏播放网页视频退出后状态栏被隐藏
使用wkWebView播放网页上的视频,播放完成后,退出视频返回到网页发现app的状态整个被隐藏了,解决方法,监听状态栏隐藏通知,在适当的时候让状态栏显示出来 [[NSNotificationCent ...
随机推荐
- Azure AD(六)添加自定义域名
一,引言 每当我们在 Azure Portal 上创建新的租户时,都会在设置租户的 "初始域名" 后加上 ".onmicrosoft.com",默认情况下 &q ...
- mysql 查询附近N公里内数据
mysql 查询一个地点(经纬度) 附近N公里内的数据.(根据一个地点的经纬度查询这个地点方圆几公里内的数据)1.创建测试表 CREATE TABLE `location` ( `id` int(10 ...
- Nginx-反向代理服务器
概述 Nginx是一款轻量级的Web服务器.反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在互联网项目中广泛应用. 优点 nginx是多进程的,不会出现并发问题,不用加锁.一个进程出问题 ...
- 实体类分层命名PO,VO,BO,DTO,POJO,DAO,DO
一.Java中PO.DO.TO.DTO. VO. BO.POJO .DAO的概念 PO:persistant object持久对象 最形象的理解就是一个PO就是数据库中的一条记录.好处是可以把一条记录 ...
- jenkins发布代码选择不同分支
jenkins上传代码分支以前都是用变量的方式,手动实现.过程就像这样 构建时候的界面就像下面这样,需要手动输入分支版本. 或者有固定的上线分支,用参数化构建 选项参数 来选择.总之这些方法比较传统, ...
- 简述LSM-Tree
LSM-Tree 1. 什么是LSM-Tree LSM-Tree 即 Log Structrued Merge Tree,这是一种分层有序,硬盘友好的数据结构.核心思想是利用磁盘顺序写性能远高于随机写 ...
- Docker 设置国内镜像源
创建或修改 /etc/docker/daemon.json 文件,修改为如下形式 # vi /etc/docker/daemon.json { "registry- ...
- petite-vue源码剖析-从静态视图开始
代码库结构介绍 examples 各种使用示例 scripts 打包发布脚本 tests 测试用例 src directives v-if等内置指令的实现 app.ts createApp函数 blo ...
- 教程6--配置ssh
配置ssh 如果需要使用到远程仓库,那么就需要两个步骤: (1)配置创建SSH key(用于识别用户,免得每次输入账号密码) 在命令窗口输入ssh-keygen -t rsa -c "你的邮 ...
- ibv_close_device()函数
int ibv_close_device(struct ibv_context *context); 描述 函数用来关闭一个RDMA设备context: 注意: 函数不能用来释放与该Context关联 ...