卸载跟踪
通过将推送通知服务与 Singular SDK 集成,跟踪应用程序的卸载情况,以衡量用户留存率并优化重新参与活动。
重要:谷歌于 2018 年 4 月废弃了 GCM API。使用 Firebase Cloud Messaging (FCM) 实现所有 Android 卸载跟踪。
安卓卸载跟踪
前提条件
在您的 Flutter 应用程序中实施卸载跟踪之前,请按照 "设置 Android 卸载跟踪 "指南在 Singular 平台中配置您的应用程序。
系统要求
卸载跟踪需要 Firebase Cloud Messaging 和特定设备配置。
FCM 要求(源):
- 安卓版本:设备必须运行 Android 4.1 (API 16) 或更高版本
- Google Play 服务:设备必须安装 Google Play Store 应用程序
- 模拟器支持:支持带有 Google API 的 Android 4.1 或更高版本模拟器
- 分发:应用程序可在 Google Play Store 之外分发,同时仍支持卸载跟踪
注意:不支持的 Android 版本或没有 Google Play 服务的设备上的用户将不会被跟踪卸载。
实施步骤
步骤 1:安装 Firebase 软件包
在pubspec.yaml 文件中添加 Firebase 依赖项,以获得核心功能和消息支持。
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.24.2
firebase_messaging: ^14.7.10
singular_flutter_sdk: ^1.8.0
添加依赖项后,运行flutter pub get 安装软件包。
第 2 步:配置 Firebase
为您的 Android Flutter 项目添加 Firebase 配置文件。
- 在 Firebase 控制台项目中注册 Android 应用程序
- 下载
google-services.json并将其放入android/app/ - 确认 Firebase 消息传递依赖项已添加到您的项目中
有关详细设置说明,请参阅将Firebase 添加到您的 Flutter 应用程序。
第 3 步:初始化 Firebase
在运行 Flutter 应用程序前初始化 Firebase,以启用消息服务。
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
runApp(MyApp());
}
第 4 步:请求通知权限
在获取 FCM 令牌之前,先向用户申请通知权限(Android 13+ 需要)。
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
Future<bool> requestNotificationPermission() async {
if (Platform.isAndroid) {
// Android 13+ requires explicit permission request
// Note: firebase_messaging handles this automatically via requestPermission
final FirebaseMessaging messaging = FirebaseMessaging.instance;
final NotificationSettings settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
return settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
}
// iOS permission handled separately
return true;
}
第5步:获取并注册FCM令牌
获取 FCM 设备令牌,并在请求权限后使用registerDeviceTokenForUninstall() 在 Singular 注册。
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
initializeAndroidUninstallTracking();
}
}
Future<void> initializeAndroidUninstallTracking() async {
try {
// Request notification permission
final hasPermission = await requestNotificationPermission();
if (!hasPermission) {
print('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get FCM token
final token = await FirebaseMessaging.instance.getToken();
if (token != null) {
// Register token with Singular for uninstall tracking
Singular.registerDeviceTokenForUninstall(token);
print('FCM token registered with Singular: $token');
} else {
print('No FCM token available');
}
} catch (error) {
print('Error setting up uninstall tracking: $error');
}
}
Future<bool> requestNotificationPermission() async {
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
return settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
方法签名:
static void registerDeviceTokenForUninstall(String token)
有关完整的方法文档,请参阅registerDeviceTokenForUninstall 参考资料。
步骤 6:处理令牌刷新
每当 FCM 令牌刷新时,就用 Singular 更新令牌,以保持准确的卸载跟踪。
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
setupTokenRefreshListener();
}
}
void setupTokenRefreshListener() {
// Listen for token refresh events
FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
print('FCM token refreshed: $token');
// Update Singular with new token
Singular.registerDeviceTokenForUninstall(token);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
最佳做法:FCM 令牌可以随时刷新(应用程序更新、设备还原等)。请始终订阅onTokenRefresh 流,以便 Singular 随时更新最新令牌。
iOS 卸载跟踪
前提条件
按照设置 iOS 卸载跟踪指南在 Singular 平台上配置 iOS 应用程序。
iOS 卸载跟踪基于苹果推送通知服务(APNs)技术。如果您的应用程序不支持推送通知,请参阅 Apple 的 "使用 APNs 注册您的应用程序 "指南。
实施步骤
第 1 步:配置 iOS 项目
在 iOS 项目中添加 Firebase 配置并启用推送通知功能。
- 在 Firebase Console 项目中注册 iOS 应用程序
- 下载
GoogleService-Info.plist并将其添加到 Xcode Runner 文件夹中 - 在 Xcode 项目设置中,启用推送通知功能
- 启用后台模式并勾选远程通知
第 2 步:请求 iOS 通知授权
请求用户的通知权限并检索 APNS 设备令牌。
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
Future<bool> requestIOSNotificationPermission() async {
if (!Platform.isIOS) {
return false;
}
try {
final FirebaseMessaging messaging = FirebaseMessaging.instance;
final NotificationSettings settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
provisional: false,
);
final authorized =
settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
if (authorized) {
print('iOS notification authorization status: ${settings.authorizationStatus}');
return true;
}
return false;
} catch (error) {
print('Error requesting iOS notification permission: $error');
return false;
}
}
第 3 步:检索和注册 APNS 令牌
获取 APNS 设备令牌,并在获得授权后使用registerDeviceTokenForUninstall() 在 Singular 注册。
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
if (Platform.isIOS) {
initializeIOSUninstallTracking();
}
}
Future<void> initializeIOSUninstallTracking() async {
try {
// Request notification authorization
final hasPermission = await requestIOSNotificationPermission();
if (!hasPermission) {
print('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get APNS token
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken != null) {
// Register token with Singular for uninstall tracking
Singular.registerDeviceTokenForUninstall(apnsToken);
print('APNS token registered with Singular: $apnsToken');
} else {
print('No APNS token available');
}
} catch (error) {
print('Error setting up iOS uninstall tracking: $error');
}
}
Future<bool> requestIOSNotificationPermission() async {
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
return settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
令牌格式:从getAPNSToken() 获取的 APNS 令牌已格式化为十六进制字符串,这是 Singular 的正确格式。
步骤 4:处理令牌刷新(iOS)
如果 APNS 令牌在应用程序生命周期中发生变化,则使用 Singular 更新令牌。
import 'package:flutter/material.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'dart:io';
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
if (Platform.isIOS) {
setupIOSTokenRefreshListener();
}
}
void setupIOSTokenRefreshListener() {
// Listen for token refresh events
FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
print('APNS token refreshed: $token');
// Update Singular with new token
Singular.registerDeviceTokenForUninstall(token);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
完整的跨平台实施
统一卸载跟踪设置
通过正确的错误处理和令牌刷新逻辑,为 Android 和 iOS 平台实施卸载跟踪。
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:singular_flutter_sdk/singular.dart';
import 'package:singular_flutter_sdk/singular_config.dart';
import 'dart:io';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
// Initialize Singular SDK
initializeSingularSDK();
// Setup uninstall tracking
initializeUninstallTracking();
// Setup token refresh listener
setupTokenRefreshListener();
}
void initializeSingularSDK() {
final config = SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
);
Singular.start(config);
}
Future<void> initializeUninstallTracking() async {
try {
if (Platform.isAndroid) {
await setupAndroidUninstallTracking();
} else if (Platform.isIOS) {
await setupIOSUninstallTracking();
}
} catch (error) {
print('Error initializing uninstall tracking: $error');
}
}
Future<void> setupAndroidUninstallTracking() async {
print('Setting up Android uninstall tracking');
// Request notification permission
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus != AuthorizationStatus.authorized &&
settings.authorizationStatus != AuthorizationStatus.provisional) {
print('Android notification permission denied');
return;
}
// Get and register FCM token
final token = await messaging.getToken();
if (token != null) {
Singular.registerDeviceTokenForUninstall(token);
print('Android FCM token registered: $token');
} else {
print('Failed to retrieve Android FCM token');
}
}
Future<void> setupIOSUninstallTracking() async {
print('Setting up iOS uninstall tracking');
// Request iOS notification authorization
final messaging = FirebaseMessaging.instance;
final settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
final authorized =
settings.authorizationStatus == AuthorizationStatus.authorized ||
settings.authorizationStatus == AuthorizationStatus.provisional;
if (!authorized) {
print('iOS notification permission denied');
return;
}
// Get and register APNS token
final apnsToken = await messaging.getAPNSToken();
if (apnsToken != null) {
Singular.registerDeviceTokenForUninstall(apnsToken);
print('iOS APNS token registered: $apnsToken');
} else {
print('Failed to retrieve iOS APNS token');
}
}
void setupTokenRefreshListener() {
// Listen for token refresh events (works for both platforms)
FirebaseMessaging.instance.onTokenRefresh.listen((String token) {
print('${Platform.operatingSystem.toUpperCase()} token refreshed: $token');
Singular.registerDeviceTokenForUninstall(token);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Uninstall Tracking Demo'),
),
body: Center(
child: Text('Uninstall tracking initialized'),
),
),
);
}
}
特定平台注意事项
- iOS 平台:确保您的应用程序拥有必要的推送通知权限,并在 Apple Developer 账户中正确配置 APN
-
安卓:验证 FCM 是否已在 Firebase 控制台中设置,以及
google-services.json文件是否已包含在项目中,网址是android/app/。
验证和故障排除
验证实施
在部署到生产环境之前,请确认卸载跟踪工作正常。
- 检查日志:验证令牌注册是否以正确格式出现在控制台日志中
- 测试令牌生成:确保在授予权限后首次启动应用程序时生成令牌
- 监控仪表板:在 24-48 小时后检查 Singular 仪表板上的卸载跟踪数据
- 测试令牌刷新:清除应用程序数据并验证重新启动应用程序时令牌更新是否正确
常见问题
-
令牌未生成:验证 Firebase 依赖项是否已正确安装,Firebase 是否已在 Flutter 项目中配置。添加依赖项后运行
flutter pub get - 权限被拒绝:检查用户是否已授予通知权限。对于 Android 13+,需要明确的权限请求。对于 iOS,用户必须授权通知
-
令牌未更新:确保您已订阅两个平台的
onTokenRefresh流。应在应用程序初始化时设置监听器 - 数据缺失:确认设备符合平台要求(Android 4.1+ 支持 Google Play 服务,iOS 支持 APN)。没有这些服务的设备无法跟踪
- 配置错误:确认已在应用程序的 Singular 平台设置中启用卸载跟踪。请按照 "先决条件 "中链接的特定平台设置指南进行操作
-
Firebase 设置:对于 Android,确保
google-services.json位于android/app/中。对于 iOS,确保GoogleService-Info.plist已添加到 Xcode 项目 Runner 文件夹中。 -
SDK 初始化:在调用
registerDeviceTokenForUninstall()之前,确认 Singular SDK 已初始化。令牌注册应在 SDK 启动后进行 -
平台检测:从
dart:io使用Platform.isAndroid和Platform.isIOS,以确保特定平台的代码在正确的平台上运行。
其他资源:有关详细的故障排除,请参阅《Android 卸载跟踪设置指南》、《iOS 卸载跟踪设置指南》和Flutter Firebase 设置文档。
最佳实践
令牌管理
- 尽早注册:在应用程序生命周期中尽早申请权限和注册令牌,最好在首次启动应用程序时就注册。
- 错误处理:围绕令牌检索和注册实施稳健的错误处理,以便从容应对故障
- 令牌刷新:始终实施令牌刷新监听器,以便在令牌发生变化时保持 Singular 更新
- 用户体验:在上下文中请求通知权限,解释应用程序需要这些权限的原因,以提高权限授予率
测试策略
- 开发测试:在安卓和 iOS 实体设备上进行测试,因为模拟器对推送通知的支持可能有限
- 权限流程:测试用户拒绝权限的场景,并验证应用程序是否能从容应对
- 令牌持久性:验证令牌在应用程序重启时是否会持续存在,并在刷新时正确更新
- 日志记录:在开发过程中启用详细日志记录,以跟踪令牌生成和注册情况