SDK 集成了 Firebase 的谷歌分析、崩溃分析和云消息功能,可以参考 Firebase 官方文档 在应用中集成 Firebase。
为了谷歌 3.0 广告投放(衡量广告收入)和收集更详细的崩溃日志,请正确集成 Firebase。
1. 添加 Firebase
1.1 集成 Firebase
请按照以下步骤集成 Firebase:
- 在 Firebase 控制台创建 Firebase 项目,并在 Firebase 中注册应用程序;
- 下载 Firebase Android 配置文件 (
google-services.json) 并将其添加到游戏工程的应用级模块的根目录下(google-services.json文件由游戏发行方提供); 在游戏工程的
根目录下的 Gradle 文件 (通常是<project>/build.gradle) 中,将 Google 服务和 Crashlytics 插件添加为构建脚本依赖项:plugins { // Make sure that you have the AGP plugin 8.1+ dependency id 'com.android.application' version '8.1.4' apply false // ... // Add the dependency for the Google services Gradle plugin id 'com.google.gms.google-services' version '4.4.4' apply false // Add the dependency for the Crashlytics Gradle plugin id 'com.google.firebase.crashlytics' version '3.0.6' apply false }游戏工程的
应用级模块的 Gradle 文件(通常是<project>/<app-module>/build.gradle)中,添加 Google 服务和 Crashlytics 插件:plugins { id 'com.android.application' // Add the Google services Gradle plugin id 'com.google.gms.google-services' // Add the Crashlytics Gradle plugin id 'com.google.firebase.crashlytics' }Firebase SDK 的依赖项目已在快速接入中引入,如果还需要使用 Firebase 的其他 SDK,请自行添加相关依赖项。
[!TIP|labelVisibility:hidden|iconVisibility:hidden] 如果 Android Gradle Plugin 没有升级到 8.1.0 及以上版本,请使用 Crashlytics Gradle 插件 v2.9.9。
1.2 使用 Firebase
SDK 对 Firebase 的Analytics、Crashlytics、Remote Config等SDK的常用方法进行了封装,可以通过以下方法获取Firebase服务并调用对应的方法:
SFFirebaseService firebaseService = SFPlatform.getInstance().getFirebaseService();
2. Firebase Analytics
Firebase Analytics的核心是 Google Analytics,Google Analytics 是一款免费的应用效果衡量解决方案,可提供关于应用使用情况和用户互动度的数据分析。
2.1 事件调试
通常,应用所记录的事件会每隔 1 小时左右集中起来作为一批进行处理,并一起上传,此方法能节省最终用户的设备电量和网络流量。但是为了验证 Analytics 实现情况(也是为了能在 DebugView 报告中查看 Analytics 信息),可以在开发设备上启用调试模式,从而以最短的延迟上传事件。启用 Analytics 调试功能后也可以在 Android Studio 控制台中查看事件报告日志。
开启调试模式
adb shell setprop debug.firebase.analytics.app <package_name>请将命令行中的
<package_name>字段替换为应用包名。数据成功上报后,可以前往 DebugView 页面查看数据。停用调试模式 调试模式将保持启用状态,直至通过执行以下命令明确将其停用:
adb shell setprop debug.firebase.analytics.app .none.
2.2 衡量广告收入
AppLovin 和 ironSource 等平台会提供广告展示级收入数据,可以使用这些广告数据来记录 ad_impression 事件。
[!TIP]
ad_impression事件必须同时添加currency和value参数,两者都应尽可能准确,以防止高估或低估用户价值。currency参数 (iOS] | Android | Unity) 应以三字母 ISO_4217 格式的字符串形式发送(例如 "USD")。有些广告变现平台会省略币种,因此您可能需要对参数进行硬编码。value参数 (iOS] | Android | Unity) 在发送时应使用句点作为小数分隔符,此值应该是一个数值类型,例如 double 或 long。注意:如果通过
Sofish SDK集成广告,SDK 内部已经记录了广告收入事件,游戏端不需要再记录Firebase的ad_impression事件!
2.2.1 AppLovin
如果接入了 AppLovin 广告变现平台,请添加以下埋点:
@Override
public void onAdRevenuePaid(MaxAd impressionData) {
double revenue = impressionData.getRevenue(); // In USD
Bundle params = new Bundle();
params.putString(FirebaseAnalytics.Param.AD_PLATFORM, "appLovin");
params.putString(FirebaseAnalytics.Param.AD_SOURCE, impressionData.getNetworkName());
params.putString(FirebaseAnalytics.Param.AD_FORMAT, impressionData.getFormat().getLabel());
params.putString(FirebaseAnalytics.Param.AD_UNIT_NAME, impressionData.getAdUnitId());
params.putDouble(FirebaseAnalytics.Param.VALUE, revenue);
params.putString(FirebaseAnalytics.Param.CURRENCY, "USD"); // All Applovin revenue is sent in USD
FirebaseAnalytics.getInstance(context).logEvent(FirebaseAnalytics.Event.AD_IMPRESSION, params);
}
2.2.2 ironSource
如果接入了 ironSource 广告变现平台,请添加以下埋点:
@Override
public void onImpressionSuccess(ImpressionData impressionData) {
// The onImpressionSuccess will be reported when the rewarded video and interstitial ad is opened.
// For banners, the impression is reported on load success.
if (impressionData != null) {
Bundle bundle = new Bundle();
bundle.putString(FirebaseAnalytics.Param.AD_PLATFORM, "ironSource");
bundle.putString(FirebaseAnalytics.Param.AD_SOURCE, impressionData.getAdNetwork());
bundle.putString(FirebaseAnalytics.Param.AD_FORMAT, impressionData.getAdUnit());
bundle.putString(FirebaseAnalytics.Param.AD_UNIT_NAME, impressionData.getInstanceName());
bundle.putString(FirebaseAnalytics.Param.CURRENCY, "USD");
bundle.putDouble(FirebaseAnalytics.Param.VALUE, impressionData.getRevenue());
FirebaseAnalytics.getInstance(context).logEvent(FirebaseAnalytics.Event.AD_IMPRESSION, bundle);
}
}
2.3 常用方法
可以通过firebaseService调用Firebase Analytics的一些常用方法。
设置默认事件参数
void setDefaultEventParameters(@Nullable Bundle parameters);设置用户属性
void setUserProperty(@NonNull @Size(min = 1L, max = 24L) String name, @Nullable @Size(max = 36L) String value);设置是否启用分析数据收集
void setAnalyticsCollectionEnabled(boolean enabled);设置会话超时时长
void setSessionTimeoutDuration(long milliseconds); // 默认值为30分钟重置分析数据
void resetAnalyticsData(); // 重置分析数据会清除设备上该应用的所有分析数据,并重置应用实例ID
3. Firebase Crashlytics
Firebase Crashlytics 是一个轻量级的实时崩溃报告解决方案,可帮助开发者对影响应用质量的稳定性问题进行跟踪、确定优先解决顺序并加以修复。 Crashlytics 会对崩溃进行智能分组并突出显示导致这些崩溃的环境因素,从而为开发者节省问题排查的时间。
3.1 自动上传映射文件
Crashlytics Gradle 插件可以自动检测代码混淆。当构建生成映射文件时,插件会上传该文件,以便 Crashlytics 服务器可以使用该文件将应用程序的堆栈跟踪呈现为未混淆且可读性更高的代码。如果需要阻止 Crashlytics Gradle 插件为使用混淆处理的变体上传映射文件,请在应用程序级模块的
build.gradle文件中将firebaseCrashlytics.mappingFileUploadEnabled属性设置为 false。这有助于缩短经过混淆处理的 build 的构建时间,但请注意,在 Firebase 控制台的 Crashlytics 页面中生成的堆栈跟踪将以经过混淆的方式显示。android { // To enable Crashlytics mapping file upload for specific build types: buildTypes { debug { ... firebaseCrashlytics { // Disable uploading mapping file to Firebase servers. mappingFileUploadEnabled false } } release { ... firebaseCrashlytics { // Enable uploading mapping file to Firebase servers. mappingFileUploadEnabled true } } } // To enable Crashlytics mapping file upload for specific product flavors: flavorDimensions "environment" productFlavors { staging { dimension "environment" ... firebaseCrashlytics { mappingFileUploadEnabled false } } prod { dimension "environment" ... firebaseCrashlytics { mappingFileUploadEnabled true } } } }如果Android应用包含原生库,为了从 NDK 崩溃中生成可读的堆栈跟踪,Crashlytics 需要了解本机二进制文件中的符号。 Crashlytics Gradle 插件包含
uploadCrashlyticsSymbolFileBUILD_VARIANT任务来自动执行此过程。为了可以访问自动符号上传任务,请确保在应用程序级模块的build.gradle文件中将其nativeSymbolUploadEnabled设置为true。android { // To enable Crashlytics native symbols upload for specific build types: buildTypes { release { ... firebaseCrashlytics { // Enable processing and uploading of native symbols to Firebase servers. // By default, this is disabled to improve build speeds. // This flag must be enabled to see properly-symbolicated native stack traces in the Crashlytics dashboard. nativeSymbolUploadEnabled true } } } }强制测试崩溃以完成设置:
- 将可以强制测试崩溃的代码添加在应用程序中;
- 在 Android studio 中构建并运行应用程序;
- 应用崩溃后,重新启动它,以便应用可以将崩溃报告发送到 Firebase;
- 转到 Firebase 控制台的 Crashlytics 仪表板以查看测试崩溃。
[!TIP] 强制测试崩溃可以在应用程序中添加一个
Test Crash按钮,点击按钮执行throw new RuntimeException("Test Crash");代码 。如果刷新 Firebase 控制台后五分钟后仍未看到测试崩溃,请启用调试日志记录以查看应用程序是否正在发送崩溃报告。
3.2 手动上传映射文件
如果自动上传失败,可以使用以下任一方法手动上传映射文件。
- 方法 1:使用基于控制台的
Drag and Drop选项上传包含映射文件的 zip 文件(转至 Firebase 控制台 > Crashlytics Mapping files 选项卡)。查找映射文件的方法如下:- 打开项目的
应用级模块的目录; - 打开
build/outputs/mapping目录,其中包含 Android Studio 编译过程中生成的mapping.txt文件。
- 打开项目的
- 方法 2:通过 Firebase CLI 工具上传:
- 按照说明安装 Firebase CLI,如果已经安装了 CLI,请确保更新到其最新版本。
- 构建完成后,生成与 Crashlytics 兼容的符号文件,并通过运行以下 Firebase CLI 命令将其上传到 Firebase 服务器:
firebase crashlytics:symbols:upload --app=FIREBASE_APP_ID PATH/TO/SYMBOLS[!TIP|labelVisibility:hidden|iconVisibility:hidden] 命令中
FIREBASE_APP_ID表示Firebase Android应用ID,PATH/TO/SYMBOLS表示CLI生成的符号文件的路径。
3.3 启用调试日志记录
如果在 Crashlytics 仪表板中没有看到测试崩溃,则可以使用 Crashlytics 的调试日志记录来帮助跟踪问题。
启用并查看 Crashlytics 的调试日志记录:
在运行应用程序之前,请将以下 adb shell 标志设置为
DEBUG:adb shell setprop log.tag.FirebaseCrashlytics DEBUG通过运行以下命令查看设备日志中的日志:
adb logcat -s FirebaseCrashlytics
强制测试崩溃。
- 在 Logcat 输出中查找
Crashlytics report upload complete消息或代码204,其中任何一个都可以验证应用是否正在向 Firebase 发送崩溃。 - 确认应用程序正在发送崩溃后,您可以选择通过将 adb shell 标志设置为
INFO来禁用调试日志记录:adb shell setprop log.tag.FirebaseCrashlytics INFO
3.4 注意事项
如果出现编译失败的问题,请检查项目所使用的 Gradle 版本,最好将 Gradle 插件版本升级到 7.2 及以上版本,避免出现 Gradle 插件版本太低导致的兼容性问题。如果 Gradle 版本低于 7.0,并且暂时无法通过升级 Gradle 版本解决编译问题的,可以尝试将 com.google.firebase:firebase-crashlytics-gradle 的版本降低为 2.8.1。
3.5 常用方法
可以通过firebaseService调用Firebase Crashlytics的一些常用方法。
记录一份非致命报告并发送给Crashlytics
void recordExceptionToCrashlytics(@NonNull Throwable throwable);设置Crashlytics与Crash和ANR报告关联的自定义键和值
void setCrashlyticsCustomKey(@NonNull String key, @NonNull String value);检查应用程序在上次运行时是否崩溃
boolean didCrashOnPreviousExecution();判断是否启用Crashlytics的自动数据收集
boolean isCrashlyticsCollectionEnabled();设置是否启用Crashlytics的自动数据收集
void setCrashlyticsCollectionEnabled(boolean enabled);
4. Firebase Cloud Messaging
Firebase Cloud Messaging (FCM) 是一种跨平台消息传递解决方案,可供应用可靠地发送消息。
4.1 消息监听
游戏在游戏内需要接入 Firebase 推送功能时,可以调用该方法监听 Firebase 推送 token,游戏服务端需要自己接入 Firebase 推送 api 进行个性化推送,SDK 只负责接入 Firebase 并将最新的 token 返回给游戏端。代码示例如下:
SFPlatform.getInstance().setFirebaseMessagingListener(new SFFirebaseMessagingListener() {
@Override
public void onFirebaseToken(@NonNull String token) {
// Firebase token 生成和更新时都会触发此回调
}
@Override
public void onMessageReceived(@NonNull SFFirebaseMessage message) {
// Firebase接收到消息时对于大多数消息类型都会触发此回调
}
});
SFFirebaseMessage 数据结构如下:
| 参数名称 | 参数类型 | 是否可为空 | 说明 |
|---|---|---|---|
| senderId | String | 是 | 消息发件人 ID |
| from | String | 是 | 消息的发件人 |
| to | String | 是 | 消息的收件人 |
| collapseKey | String | 是 | 消息的折叠键 |
| messageId | String | 是 | 消息 ID |
| messageType | String | 是 | 消息类型 |
| sentTime | Long | 否 | 消息发送时间(以毫秒为单位) |
| ttl | Int | 否 | 消息生存时间 (以秒为单位) |
| originalPriority | Int | 否 | 原始消息优先级(0:未知;1:高;2:普通) |
| priority | Int | 否 | 传递的消息优先级(0:未知;1:高;2:普通) |
| data | Map | 否 | 消息负载数据(键值对) |
| notificationInfo | Object | 是 | 通知显示信息 |
对于大多数消息类型,都可以使用 onMessageReceived() 处理,但以下情况除外:
- 当应用在后台时送达的通知消息:在这种情况下,通知将传送至设备的系统任务栏。默认情况下,用户点按通知即可打开应用启动器。
- 在后台接收的既包含通知又具有数据载荷的消息:在这种情况下,通知将传送至设备的系统任务栏,数据载荷则传送至启动器 Activity 的 intent 的 extras 属性中。
4.2 通知样式
如果需要设置默认值以便自定义通知的外观,可以指定自定义默认图标和颜色,当通知载荷中未设置相应的值时,系统会使用这些默认值。可以在 AndroidManifest.xml 的 application 标签中设置自定义默认图标和自定义颜色:
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_stat_ic_notification" />
<!-- Set custom default color. This is used when no color is set for the incoming notification message. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />
4.3 通知渠道
从 Android 8.0(API 级别 26)及更高版本开始,支持并推荐使用通知渠道。FCM 提供具有基本设置的默认通知渠道。如果需要创建和使用自己的默认通道,可以在 AndroidManifest.xml 的 application 标签中设置 default_notification_channel_id 作为通知通道对象的 ID,只要传入消息未明确设置通知通道,FCM 就会使用此值:
<!-- Set custom notification channel. This is used when no channel is set for incoming notification messages. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
4.4 通知权限
Android 13 中引入了用于显示通知的新运行时权限,该项引入会影响在 Android 13 或更高版本上使用 FCM 通知的所有应用。默认情况下,FCM SDK(23.0.6 或更高版本)中包含清单中定义的 POST_NOTIFICATIONS 权限。不过应用还需要在运行时请求android.permission.POST_NOTIFICATIONS权限。在用户授予此权限之前,应用将无法显示通知。如需请求该项运行时权限,请执行以下操作:
// Declare the launcher at the top of your Activity/Fragment:
private final ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
if (isGranted) {
// FCM SDK (and your app) can post notifications.
} else {
// TODO: Inform user that that your app will not show notifications.
}
});
private void askNotificationPermission() {
// This is only necessary for API level >= 33 (TIRAMISU)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
// FCM SDK (and your app) can post notifications.
} else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
// TODO: display an educational UI explaining to the user the features that will be enabled by them granting the POST_NOTIFICATION permission.
// This UI should provide the user "OK" and "No thanks" buttons. If the user selects "OK," directly request the permission.
// If the user selects "No thanks," allow the user to continue without notifications.
} else {
// Directly ask for the permission
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
}
}
}
默认情况下,FCM SDK 包含 POST_NOTIFICATIONS 权限。如果应用不使用通知消息(无论是通过 FCM 通知、通过其他 SDK 还是由应用直接发布),并且不想让应用包含该权限,则可以使用清单合并的 remove 标记移除该权限。请注意,移除此权限会阻止系统显示所有通知,而不仅仅是 FCM 通知。将以下内容添加到应用的清单文件中:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" tools:node="remove"/>
4.5 卸载指标衡量
AppsFlyer 使用 Firebase 云消息(FCM)传递在 Android 应用中设置卸载指标衡量,请按照以下步骤完成设置。
4.5.1 将FCM连接到AppsFlyer
请按照以下步骤完成配置 FCM HTTP V1:
- 获取项目ID:进入FCM控制台,选择一个项目,转到
Project Overview > Project Settings,复制Project ID。 - 启用FCM API:转到
Cloud Messaging选项卡,请确保Firebase Cloud Messaging API (V1)已启用。 - 创建角色:转到
Service accounts > Manage service account permissions选项卡,将打开一个新的浏览器标签页。- 在侧边菜单中,选择
Roles > +Create role开始创建角色。 Title和ID可以分别输入AppsFlyer uninstalls和af_uninstalls。- 点击
Add permissions,在Filter中选择cloudmessaging.messages.create权限,点击Add完成权限添加。 - 点击
Create完成角色创建。
- 在侧边菜单中,选择
- 为AppsFlyer分配FCM卸载角色:在侧边菜单中,选择
IAM,打开PERMISSIONS > VIEW BY PRINCIPALS选项卡,点击Grant Access,在Add Principals > New principals中输入af-uninstalls-tracking@af-uninstalls-tracking.iam.gserviceaccount.com,在Assign roles -> Role中选择创建的角色AppsFlyer Uninstalls,点击Save。
[!TIP|labelVisibility:hidden|iconVisibility:hidden] AppsFlyer 使用静默推送通知仅用于衡量卸载量或识别不活跃用户,不会将其用于任何其他目的。
4.5.2 在AppsFlyer中配置卸载衡量指标
请按照以下步骤在AppsFlyer中配置卸载指标:
- 在AppsFlyer 中,从侧边菜单中选择
Settings > App Settings,如有必要,请启用Uninstall measurement功能。 - 选择
HTTP V1,并在Project ID输入框中填写之前准备的项目ID。 - 点击
Test connection将显示成功消息,点击Save settings。
[!TIP|labelVisibility:hidden|iconVisibility:hidden] 如果在测试连接后收到“Project ID is invalid”或“Access permissions are missing”的错误消息,请检查 FCM 中的连接设置。
4.5.3 测试卸载测量
可以对以下类型的应用执行此测试:
- Google Play 商店中已上架的应用
- 审核中的应用(尚未在 Google Play 商店列出)
- 通过直接下载提供的应用
- 第三方应用商店中的应用
测试 Android 应用时请注意以下事项:
- 卸载衡量是按日处理的,卸载事件会在 24 小时内记录,但如果应用在此期间被重新安装,则不会记录该事件。
- 卸载事件最多需要 48 小时才会显示在原始数据报告以及 AppsFlyer 控制面板(汇总绩效报告)中。
4.5.4 卸载事件映射
与广告平台共享卸载数据,需要将 af_uninstall 事件映射给合作伙伴。与常规应用内事件的回传不同,卸载事件并非实时发送。报告中显示的事件时间代表 AppsFlyer 判定应用已被卸载的时间,而非实际卸载的时间。
请记住以下几点:
- 发送前提:只有当事件真实发生并被 AppsFlyer 记录时,AppsFlyer 才能发送回传。
- 数据一致性:如果在在仪表板概览页面或原始数据报告中看不到卸载操作,则表示即使映射了
af_uninstall事件,卸载回传也没有发送给合作伙伴。
局限性
- 卸载事件不包含在应用内事件回传报告中。
- 所有合作伙伴均支持
af_uninstall事件。如果想将其映射到某个合作伙伴,但该事件未在合作伙伴的集成选项卡中显示,请直接联系该合作伙伴以获取支持。
4.5.5 关闭卸载测量
如果提供了Firebase服务器密钥,则默认情况下会启用应用卸载指标,应用所有者可以通过控制面板禁用此功能。要报告卸载操作,必须在安装应用时启用Enable uninstall measurement功能,如果该功能处于关闭状态,则不会报告卸载操作。如果需要关闭卸载测量,请按照以下步骤操作:
- 在AppsFlyer 中,从侧边菜单中选择
Settings > App Settings。 - 转到
Attribution > Uninstall measurement,然后关闭Enable uninstall measurement。
5. Firebase Remote Config
Firebase Remote Config 是一项云服务,让开发者可以更改客户端应用或服务器的行为和外观,而无需用户下载应用更新。使用 Remote Config 时,可以创建应用内默认值,用于控制应用的行为和外观。之后,可以使用 Firebase 控制台或 Remote Config 后端 API 为所有 Remote Config API 用户或各个细分用户群替换这些应用内默认值。
不要在 Remote Config 参数键或值中存储机密数据。Remote Config 数据在传输过程中会加密,但最终用户可以访问其客户端应用实例可用的任何默认或提取的 Remote Config 参数。
5.1 默认参数值
可以在 Remote Config 对象中设置应用内默认参数值,以便应用在连接到 Remote Config 后端之前能够按预期运行,并且保证在后端中未设置任何值时可以使用默认值。在应用发布时,可以从Firebase 控制台下载一个包含所有默认值的XML文件,并添加到应用级模块的资源目录(通常是<project>/<app-module>/src/main/res/xml)中,请保持文件名为remote_config_defaults.xml,为了保证默认配置生效,请不要随意修改文件名。
5.2 常用方法
可以通过firebaseService调用Firebase Remote Config的一些常用方法。
判断Firebase Remote Config是否加载完成
boolean isRemoteConfigLoaded();添加Firebase Remote Config监听
void addRemoteConfigListener(SFFirebaseRemoteConfigListener listener);返回给定key的int类型参数值
int getIntRemoteConfig(String key);返回给定key的long类型参数值
long getLongRemoteConfig(String key);返回给定key的float类型参数值
float getFloatRemoteConfig(String key);返回给定key的double类型参数值
double getDoubleRemoteConfig(String key);返回给定key的boolean类型参数值
boolean getBooleanRemoteConfig(String key);返回给定key的String类型参数值
String getStringRemoteConfig(String key);返回全部参数值
Map<String, SFRemoteConfigValue> getAllRemoteConfigs();
6. 获取 Firebase SDK属性
SFPlatform.getInstance().getFirebaseData(); // 返回结果可能为空,使用时需要进行判空校验