卸载跟踪
通过将推送通知服务与 Singular SDK 集成,跟踪应用程序的卸载情况,以衡量用户留存率并优化重新参与活动。
重要:谷歌已于 2018 年 4 月弃用 GCM API。 所有安卓卸载跟踪实施均使用 Firebase Cloud Messaging (FCM)。
安卓卸载跟踪
前提条件
在 React Native 应用程序中实施卸载跟踪之前,请按照 "设置 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 软件包
为核心功能和消息支持添加 React Native Firebase 依赖项。
npm install @react-native-firebase/app
npm install @react-native-firebase/messaging
第 2 步:配置 Firebase
将 Firebase 配置文件添加到您的 React Native Android 项目中。
- 在 Firebase 控制台项目中注册 Android 应用程序
-
下载
google-services.json并将其放入android/app/ - 确认 Firebase 消息传递依赖项已添加到您的项目中
有关详细设置说明,请参阅React Native Firebase Android 设置。
第 3 步:请求通知权限
在获取 FCM 令牌之前,向用户申请通知权限(Android 13+ 需要)。
import messaging from '@react-native-firebase/messaging';
import { Platform, PermissionsAndroid } from 'react-native';
async function requestNotificationPermission() {
if (Platform.OS === 'android') {
if (Platform.Version >= 33) {
// Android 13+ requires explicit permission request
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
// Android 12 and below automatically have permission
return true;
}
// iOS permission handled separately
return true;
}
第 4 步:获取并注册 FCM 令牌
请求权限后,使用setUninstallToken() 获取 FCM 设备令牌并在 Singular 注册。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
initializeAndroidUninstallTracking();
}
}, []);
async function initializeAndroidUninstallTracking() {
try {
// Request notification permission (Android 13+)
const hasPermission = await requestNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get FCM token
const token = await messaging().getToken();
if (token) {
// Register token with Singular for uninstall tracking
NativeSingular.setUninstallToken(token);
console.log('FCM token registered with Singular:', token);
} else {
console.warn('No FCM token available');
}
} catch (error) {
console.error('Error setting up uninstall tracking:', error);
}
}
async function requestNotificationPermission() {
// Implementation from Step 3 above
if (Platform.Version >= 33) {
const { PermissionsAndroid } = require('react-native');
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return true;
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
initializeAndroidUninstallTracking();
}
}, []);
async function initializeAndroidUninstallTracking() {
try {
// Request notification permission (Android 13+)
const hasPermission = await requestNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Get FCM token
const token = await messaging().getToken();
if (token) {
// Register token with Singular for uninstall tracking
Singular.setUninstallToken(token);
console.log('FCM token registered with Singular:', token);
} else {
console.warn('No FCM token available');
}
} catch (error) {
console.error('Error setting up uninstall tracking:', error);
}
}
async function requestNotificationPermission() {
// Implementation from Step 3 above
if (Platform.Version >= 33) {
const { PermissionsAndroid } = require('react-native');
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return true;
}
return (
// Your app components
);
}
有关完整的方法文档,请参阅setUninstallToken 参考资料。
步骤 5:处理令牌刷新
每当FCM令牌刷新时,就用Singular更新令牌,以保持准确的卸载跟踪。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('FCM token refreshed:', token);
// Update Singular with new token
NativeSingular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'android') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('FCM token refreshed:', token);
// Update Singular with new token
Singular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
);
}
最佳做法:FCM 令牌可以随时刷新(应用程序更新、设备还原等)。请始终订阅onTokenRefresh 事件,以便用最新令牌保持 Singular 的更新。
iOS 卸载跟踪
前提条件
按照《设置 iOS 卸载跟踪》指南在 Singular 平台上配置 iOS 应用程序。
iOS 卸载跟踪基于苹果推送通知服务(APNs)技术。如果您的应用程序不支持推送通知,请参阅 Apple 的 "使用 APNs 注册您的应用程序 "指南。
实施步骤
步骤 1:请求 iOS 通知授权
向用户请求通知权限并获取 APNS 设备令牌。
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
async function requestIOSNotificationPermission() {
if (Platform.OS !== 'ios') {
return false;
}
try {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('iOS notification authorization status:', authStatus);
return true;
}
return false;
} catch (error) {
console.error('Error requesting iOS notification permission:', error);
return false;
}
}
步骤 2:检索和注册 APNS 令牌
获取 APNS 设备令牌,并在获得授权后使用setUninstallToken() 在 Singular 注册。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
initializeIOSUninstallTracking();
}
}, []);
async function initializeIOSUninstallTracking() {
try {
// Request notification authorization
const hasPermission = await requestIOSNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Register for remote notifications (required for APNS)
await messaging().registerDeviceForRemoteMessages();
// Get APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
// Register token with Singular for uninstall tracking
NativeSingular.setUninstallToken(apnsToken);
console.log('APNS token registered with Singular:', apnsToken);
} else {
console.warn('No APNS token available');
}
} catch (error) {
console.error('Error setting up iOS uninstall tracking:', error);
}
}
async function requestIOSNotificationPermission() {
// Implementation from Step 1 above
const authStatus = await messaging().requestPermission();
return authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
initializeIOSUninstallTracking();
}
}, []);
async function initializeIOSUninstallTracking() {
try {
// Request notification authorization
const hasPermission = await requestIOSNotificationPermission();
if (!hasPermission) {
console.warn('Notification permission denied - uninstall tracking unavailable');
return;
}
// Register for remote notifications (required for APNS)
await messaging().registerDeviceForRemoteMessages();
// Get APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
// Register token with Singular for uninstall tracking
Singular.setUninstallToken(apnsToken);
console.log('APNS token registered with Singular:', apnsToken);
} else {
console.warn('No APNS token available');
}
} catch (error) {
console.error('Error setting up iOS uninstall tracking:', error);
}
}
async function requestIOSNotificationPermission() {
// Implementation from Step 1 above
const authStatus = await messaging().requestPermission();
return authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
}
return (
// Your app components
);
}
令牌格式:从getAPNSToken() 获取的 APNS 令牌已格式化为十六进制字符串,这是 Singular 的正确格式。
步骤 3:处理令牌刷新(iOS)
如果 APNS 令牌在应用程序生命周期内发生变化,则使用 Singular 更新令牌。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('APNS token refreshed:', token);
// Update Singular with new token
NativeSingular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export default function App() {
useEffect(() => {
if (Platform.OS === 'ios') {
// Set up token refresh listener
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log('APNS token refreshed:', token);
// Update Singular with new token
Singular.setUninstallToken(token);
});
// Clean up listener on unmount
return () => unsubscribe();
}
}, []);
return (
// Your app components
);
}
完整的跨平台实施
统一卸载跟踪设置
通过正确的错误处理和令牌刷新逻辑,为 Android 和 iOS 平台实施卸载跟踪。
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/jsNativeSingular';
import messaging from '@react-native-firebase/messaging';
import { Platform, PermissionsAndroid } from 'react-native';
export default function App() {
useEffect(() => {
initializeUninstallTracking();
const unsubscribe = setupTokenRefreshListener();
// Clean up listener on unmount
return () => {
if (typeof unsubscribe === 'function') unsubscribe();
};
}, []);
async function initializeUninstallTracking() {
try {
if (Platform.OS === 'android') {
await setupAndroidUninstallTracking();
} else if (Platform.OS === 'ios') {
await setupIOSUninstallTracking();
}
} catch (error) {
console.error('Error initializing uninstall tracking:', error);
}
}
async function setupAndroidUninstallTracking() {
// Request permission for Android 13+
if (Platform.Version >= 33) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
console.warn('Android notification permission denied');
return;
}
}
// Get and register FCM token
const token = await messaging().getToken();
if (token) {
NativeSingular.setUninstallToken(token);
console.log('Android FCM token registered:', token);
} else {
console.warn('Failed to retrieve Android FCM token');
}
}
async function setupIOSUninstallTracking() {
// Request iOS notification authorization
const authStatus = await messaging().requestPermission();
const authorized =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (!authorized) {
console.warn('iOS notification permission denied');
return;
}
// Register for remote notifications
await messaging().registerDeviceForRemoteMessages();
// Get and register APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
NativeSingular.setUninstallToken(apnsToken);
console.log('iOS APNS token registered:', apnsToken);
} else {
console.warn('Failed to retrieve iOS APNS token');
}
}
function setupTokenRefreshListener() {
// Listen for token refresh events
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log(`${Platform.OS.toUpperCase()} token refreshed:`, token);
NativeSingular.setUninstallToken(token);
});
return unsubscribe;
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
import { Platform, PermissionsAndroid } from 'react-native';
export default function App() {
useEffect(() => {
initializeUninstallTracking();
setupTokenRefreshListener();
}, []);
async function initializeUninstallTracking() {
try {
if (Platform.OS === 'android') {
await setupAndroidUninstallTracking();
} else if (Platform.OS === 'ios') {
await setupIOSUninstallTracking();
}
} catch (error) {
console.error('Error initializing uninstall tracking:', error);
}
}
async function setupAndroidUninstallTracking() {
// Request permission for Android 13+
if (Platform.Version >= 33) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
console.warn('Android notification permission denied');
return;
}
}
// Get and register FCM token
const token = await messaging().getToken();
if (token) {
Singular.setUninstallToken(token);
console.log('Android FCM token registered:', token);
} else {
console.warn('Failed to retrieve Android FCM token');
}
}
async function setupIOSUninstallTracking() {
// Request iOS notification authorization
const authStatus = await messaging().requestPermission();
const authorized =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (!authorized) {
console.warn('iOS notification permission denied');
return;
}
// Register for remote notifications
await messaging().registerDeviceForRemoteMessages();
// Get and register APNS token
const apnsToken = await messaging().getAPNSToken();
if (apnsToken) {
Singular.setUninstallToken(apnsToken);
console.log('iOS APNS token registered:', apnsToken);
} else {
console.warn('Failed to retrieve iOS APNS token');
}
}
function setupTokenRefreshListener() {
// Listen for token refresh events
const unsubscribe = messaging().onTokenRefresh((token) => {
console.log(`${Platform.OS.toUpperCase()} token refreshed:`, token);
Singular.setUninstallToken(token);
});
return unsubscribe;
}
return (
// Your app components
);
}
验证和故障排除
验证实施
在部署到生产环境之前,确认卸载跟踪工作正常。
- 检查日志:验证令牌注册是否以正确格式出现在控制台日志中
- 测试令牌生成:确保在授予权限后首次启动应用程序时生成令牌
- 监控仪表板:在 24-48 小时后检查 Singular 仪表板上的卸载跟踪数据
- 测试令牌刷新:清除应用程序数据并验证重新启动应用程序时令牌更新是否正确
常见问题
- 令牌未生成:验证 Firebase 依赖项是否已正确安装,Firebase 是否已配置到 React Native 项目中
- 权限被拒绝:检查用户是否授予了通知权限(Android 13+ 和所有 iOS 版本都需要
-
令牌未更新:确保您已订阅两个平台的
onTokenRefresh事件 - 数据缺失:确认设备符合平台要求(Android 4.1+ 支持 Google Play 服务,iOS 支持 APNs
- 配置错误:确认已在应用程序的 Singular 平台设置中启用卸载跟踪功能
-
Firebase 设置:对于 Android,请确保
google-services.json位于android/app/中。对于 iOS,请确保GoogleService-Info.plist已添加到 Xcode 项目中。
其他资源:有关详细的故障排除,请参阅《Android 卸载跟踪设置指南》、《iOS 卸载跟踪设置指南》和React Native Firebase 消息文档。