可在Classic/VPC环境下使用。
该功能可使开发人员高效地将消息从服务器或云端发送到移动设备。使用推送功能可以向移动应用和网页应用发送通知消息。推送功能支持iOS、Android应用程序等多个平台。
设置仪表盘
若要使用推送服务,首先需要要准备各平台的密钥,然后在仪表盘中进行设置。
FCM推送密钥创建方法
如需发放FCM推送密钥,请按以下步骤按顺序操作。
-
点击Firebase Console > 项目设置 > 云推送 > 服务账户管理。

-
Google Cloud > 服务账户 > 选择创建的邮件账户。

-
点击密钥 > 添加密钥 > 新建密钥 > 选择“JSON”作为密钥类型,然后点击 [创建] 按钮。
- 如果存在已创建的密钥,也可使用相应文件。

- 如果存在已创建的密钥,也可使用相应文件。
APNS认证密钥创建方法
发送iOS推送通知需要APNs认证密钥,请按照以下步骤发放。
-
登录Apple开发人员网站 > Certificates, IDs & Profiles > 选择密钥 > 点击 [+] 按钮新建密钥。
- 最多可创建2个推送认证密钥。

- 最多可创建2个推送认证密钥。
-
输入密钥名称,并激活Apple Push notifications service(APNs)。

-
点击 [Register] 按钮发放认证密钥。

-
查看密钥ID并下载。
- 下载的文件的扩展名为.p8。
- 密钥文件仅可下载一次。

仪表盘密钥设置方法
获得的密钥需要在GAMEPOT仪表板中注册,才能正常发送推送。
-
项目设置 - 关联 - Google Android(FCM)Configuration
- Sender ID:Firebase控制台 > 项目设置 > 云推送 > 发送人ID
- 私钥(json文件):使用记事本或文本编辑器等程序打开根据FCM推送密钥创建方法创建的JSON文件后,复制全部内容并注册。
-
项目设置 - 关联 - Apple iOS(APNs)Configuration
- 证书:使用记事本或文本编辑器等程序打开根据认证密钥创建方法创建的.p8文件后,复制全部内容并注册。
- Key ID:注册根据APNS认证密钥创建方法创建的.p8文件的Key ID。
- Team ID:输入Apple开发人员网站“会员详情”中的Team ID。
- Bundle ID:输入应用包的Bundle Identifier值。
推送设置方法
需在各平台完成SDK设置,才能正常接收推送通知。
AOS Native
如需使用GAMEPOT Android SDK,请按照以下步骤配置推送集成。
FirebaseMessaging集成
若要接收Android推送,需要集成FCM(Firebase Cloud Messaging)。若要集成FCM,请按照以下步骤操作。
-
Firebase项目创建及应用注册
- 在Firebase控制台创建新项目。
- 在项目中添加Android应用,并输入包名。
- 下载
google-services.json文件,并将其添加到应用模块的根目录。
-
build.gradle设置
- 在模块
app的build.gradle.kts中添加下方插件和依赖项。
- Kotlin:
plugins { id("com.google.gms.google-services") } dependencies { implementation("com.google.firebase:firebase-messaging-ktx:23.2.1") }- 在项目根的
build.gradle.kts中添加下方插件。
- Kotlin:
plugins { id("com.google.gms.google-services") version "4.4.1" apply false } - 在模块
-
FCM重置及Token管理
- 运行应用时,FCM会自动初始化,并发放设备Token。
- Token注册到服务器后,用于发送推送消息。
-
解决问题
- 如出现
Default FirebaseApp is not initialized错误,请再次确认gradle设置。 - 如
google-services.json文件不存在,请在Firebase控制台重新下载后添加到正确位置。
- 如出现
设置推送通知图标
可设置接收推送消息时要显示于通知栏的小图标。如果不另行设置,则使用包含在SDK的默认图片,也可自行设置适合游戏的图标。
若使用Android Asset Studio制作,将自动按文件夹创建图像,非常方便。
设置推送通知图标的方法如下。
- 在以下路径下分别创建
res/drawable文件夹后,添加对应尺寸的图标图片文件。- 下表介绍了按分辨率划分的文件夹及推荐图标尺寸。
| 文件夹名 | 长度 |
|---|---|
| res/drawable-mdpi/ | 24x24 |
| res/drawable-hdpi/ | 36x36 |
| res/drawable-xhdpi/ | 48x48 |
| res/drawable-xxhdpi/ | 72x72 |
| res/drawable-xxxhdpi/ | 96x96 |
- 将图片文件名变更为
ic_stat_nbase_small。
iOS Native
如需使用GAMEPOT iOS SDK,请按照以下步骤配置推送集成。
重置推送功能
若要启用推送功能,请通过AppDelegate配置通知权限请求和Token注册逻辑。
Swift和Objective-C的AppDelegate设置示例分别如下所示。
- Swift:
import NBase
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
registerForRemoteNotifications()
return true
}
func registerForRemoteNotifications() {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
if granted {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
} else {
print("The push notification permission has been denied")
}
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
NBase.setPushToken(token: token)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
NBase.setPushToken(token: "")
}
- Objective-C:
#import "[ProjectName]-Swift.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] registerForRemoteNotifications];
});
} else {
NSLog(@"Push permission denied");
}
}];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSString *token = [self stringWithDeviceToken:deviceToken];
[NBaseBridge.shared setPushToken:token];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"Failed to register for remote notifications: %@", error);
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
completionHandler(UNNotificationPresentationOptionBanner |
UNNotificationPresentationOptionSound |
UNNotificationPresentationOptionBadge);
}
- (NSString *)stringWithDeviceToken:(NSData *)deviceToken {
NSUInteger length = deviceToken.length;
if (length == 0) {
return nil;
}
const unsigned char *buffer = deviceToken.bytes;
NSMutableString *hexString = [NSMutableString stringWithCapacity:(length * 2)];
for (int i = 0; i < length; ++i) {
[hexString appendFormat:@"%02x", buffer[i]];
}
return [hexString copy];
}
图片推送
若要在iOS应用中接收并处理通知图片,须添加通知服务扩展程序。
- Swift:
- 在Xcode项目的TARGETS中添加Notification Service Extension。
- 对已创建的Notification Service Extension模块的
NotificationService.swift文件作出如下修改。
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
guard let bestAttemptContent = bestAttemptContent,
let userInfo = request.content.userInfo as? [String: Any] else {
contentHandler(request.content)
return
}
let imageURL = userInfo["imageUrl"] as? String ??
userInfo["gcm.notification.image"] as? String
guard let imageURL = imageURL, !imageURL.isEmpty else {
contentHandler(bestAttemptContent)
return
}
downloadAndAttachImage(urlString: imageURL) { attachment in
if let attachment = attachment {
bestAttemptContent.attachments = [attachment]
}
DispatchQueue.main.async {
contentHandler(bestAttemptContent)
}
}
}
private func downloadAndAttachImage(urlString: String, completionHandler: @escaping (UNNotificationAttachment?) -> Void) {
guard let url = URL(string: urlString.trimmingCharacters(in: .whitespaces)) else {
completionHandler(nil)
return
}
let session = URLSession(configuration: .ephemeral)
let task = session.downloadTask(with: url) { (location, response, error) in
if let error = error {
completionHandler(nil)
return
}
guard let location = location else {
completionHandler(nil)
return
}
let fileManager = FileManager.default
let tmpDirectory = NSTemporaryDirectory()
let tmpFile = "image_\(Date().timeIntervalSince1970).png"
let tmpPath = (tmpDirectory as NSString).appendingPathComponent(tmpFile)
let tmpURL = URL(fileURLWithPath: tmpPath)
do {
if fileManager.fileExists(atPath: tmpPath) {
try fileManager.removeItem(atPath: tmpPath)
}
try fileManager.moveItem(at: location, to: tmpURL)
let attachment = try UNNotificationAttachment(
identifier: UUID().uuidString,
url: tmpURL,
options: [UNNotificationAttachmentOptionsTypeHintKey: "public.png"]
)
completionHandler(attachment)
} catch {
completionHandler(nil)
}
}
task.resume()
DispatchQueue.main.asyncAfter(deadline: .now() + 25) {
task.cancel()
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
- Objective-C:
- 在Xcode项目的TARGETS中添加Notification Service Extension。
- 对已创建的Notification Service Extension模块的
NotificationService.m文件作出如下修改。
#import "NotificationService.h"
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
NSDictionary *userInfo = request.content.userInfo;
NSString *imageUrl = userInfo[@"imageUrl"];
if (!imageUrl) {
imageUrl = userInfo[@"gcm.notification.image"];
}
if (imageUrl && imageUrl.length > 0) {
[self downloadAndAttachImage:imageUrl completionHandler:^(UNNotificationAttachment *attachment) {
if (attachment) {
self.bestAttemptContent.attachments = @[attachment];
}
self.contentHandler(self.bestAttemptContent);
}];
} else {
self.contentHandler(self.bestAttemptContent);
}
}
- (void)downloadAndAttachImage:(NSString *)urlString completionHandler:(void(^)(UNNotificationAttachment *attachment))completionHandler {
NSURL *url = [NSURL URLWithString:[urlString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
if (!url) {
completionHandler(nil);
return;
}
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
[[session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (error) {
completionHandler(nil);
return;
}
if (!location) {
completionHandler(nil);
return;
}
NSString *tmpDirectory = NSTemporaryDirectory();
NSString *tmpFile = [NSString stringWithFormat:@"image_%f.png", [[NSDate date] timeIntervalSince1970]];
NSString *tmpPath = [tmpDirectory stringByAppendingPathComponent:tmpFile];
NSURL *tmpURL = [NSURL fileURLWithPath:tmpPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:tmpPath]) {
[fileManager removeItemAtPath:tmpPath error:nil];
}
NSError *moveError = nil;
[fileManager moveItemAtURL:location toURL:tmpURL error:&moveError];
if (moveError) {
completionHandler(nil);
return;
}
NSError *attachmentError = nil;
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:[[NSUUID UUID] UUIDString]
URL:tmpURL
options:@{UNNotificationAttachmentOptionsTypeHintKey: @"public.png"}
error:&attachmentError];
if (attachmentError) {
completionHandler(nil);
return;
}
completionHandler(attachment);
}] resume];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[session invalidateAndCancel];
});
}
- (void)serviceExtensionTimeWillExpire {
if (self.contentHandler && self.bestAttemptContent) {
self.contentHandler(self.bestAttemptContent);
}
}
Unity
在基于Unity的项目中,需在同时考虑Android和iOS构建的基础上设置配置文件和依赖项。
添加firebase配置文件
若要在Unity中使用Android推送通知(Firebase Messaging),请如下准备firebase配置文件。
- 在Firebase控制台中注册Android应用,并下载
google-services.json文件。 - 将下载得到的
google-services.json文件复制到Unity项目的./Assets件夹中。- 路径示例:
./Assets/google-services.json
- 路径示例:
添加Unity FCM SDK
若要在Unity中集成FCM SDK,应按以下方式添加库。
- 在
/Assets/NBaseSDK/Editor/NBaseSDKDependencies.xml路径中添加firebase-messaging-ktx库。
- XML:
<dependencies>
<androidPackages>
<androidPackage spec="io.nbase:nbasesdk:3.0.xx"/>
<androidPackage spec="com.google.firebase:firebase-messaging-ktx:23.2.1" />
</androidPackages>
<iosPods>
</iosPods>
</dependencies>
- 点击
Assets > External Dependency Manager > Android Resolver > Force Resolve,强制应用库依赖相。
设置推送通知图标(Unity)
接收Unity推送消息时,可设置要显示于通知栏的小图标。如果不另行设置,则使用包含在SDK的默认图片,也可自行设置适合游戏的图标。
若使用Android Asset Studio制作,将自动按文件夹创建图像,非常方便。
在Unity引擎202x及更高版本中,设置推送通知图标的方法如下。
- 在以下路径下分别创建
res/drawable文件夹后,添加对应尺寸的图标图片文件。- 下表介绍了资源路径和分辨率。
| 文件夹名 | 长度 |
|---|---|
| /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-mdpi/ | 24x24 |
| /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-hdpi/ | 36x36 |
| /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-xhdpi/ | 48x48 |
| /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-xxhdpi/ | 72x72 |
| /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-xxxhdpi/ | 96x96 |
- 在
mainTemplate.gradle中添加相应库。
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
添加implementation project('GamePotResources.androidlib') //
...
}
- 将图片文件名变更为
ic_stat_nbase_small。
推送状态
请在应用内实现状态变更功能,以便用户自主管理是否同意接受推送。
设置推送状态
须将推送状态设为true才能接收推送。用于变更推送状态的各参数含义如下。
参数描述
- push:是否同意接收推送(true/false)
- night:是否同意夜间推送(以韩国时间为准,true/false)
- ad:是否同意广告类推送(true/false) 如果广告类推送值设为false,无论是否设置了一般/夜间推送,都不会发送推送。
- token:推送Token值
如要变更接收推送状态的设置,请在登录后调用下列代码。
- Kotlin:
val pushToken = NBase.getPushToken()
val pushState = com.nbase.sdk.model.PushState(
push = enable,
night = night,
ad = ad,
token = pushToken
)
NBase.setPushState(pushState) { status, e ->
if (e != null) {
// failed.
} else {
// succeeded.
}
}
- Java:
String pushToken = _NBase.getPushToken();
com.nbase.sdk.model.PushState pushState = new com.nbase.sdk.model.PushState(
Boolean.parseBoolean(enable),
Boolean.parseBoolean(night),
Boolean.parseBoolean(ad),
pushToken
);
NBase nBase = NBase.INSTANCE;
nBase.setPushState(pushState, (status, e) -> {
if (e != null) {
// failed.
} else {
// succeeded.
}
return null;
});
- Swift:
NBase.setPushState(enable: enable, ad: ad, night: night, token: NBase.getPushToken()) { result in
switch result {
case .success:
// succeeded.
case .failure:
// failed.
}
}
- Objective-C:
BOOL enable = YES;
BOOL night = NO;
BOOL ad = YES;
NSString *token = [NBaseBridge.shared getPushToken];
[NBaseBridge.shared setPushState:enable night:night ad:ad token:token :^(NSDictionary * _Nullable result, NSError * _Nullable error) {
if (error) {
// failed.
} else {
// succeeded.
}
}];
- C#:
bool push = true;
bool ad = true;
bool night = false;
string token = ""; //如果填入空值,则保留现有Token
NBaseSDK.NBase.setPushState(push, ad, night, token, (pushState, error) => {
if (error != null)
{
// failed.
}
else
{
// succeeded.
}
});
确认推送状态
如要确认用户当前是否同意推送,请参考下方示例。
- Kotlin:
NBase.getPushState() { state, e ->
if (e != null) {
// failed.
} else {
// succeeded.
}
}
- Java:
NBase nBase = NBase.INSTANCE;
nBase.getPushState((state, e) -> {
if (e != null) {
// failed.
} else {
// succeeded.
}
return null;
});
- Swift:
NBase.getPushState() { result in
switch result {
case .success:
// succeeded.
case .failure:
// failed.
}
}
- Objective-C:
[NBaseBridge.shared getPushState:^(NSDictionary * _Nullable result, NSError * _Nullable error) {
if (error) {
// failed.
} else {
// succeeded.
}
}];
- C#:
NBaseSDK.NBase.getPushState((pushState, error) => {
if (error != null)
{
// failed.
}
else
{
// succeeded.
}
});
推送测试方法(移动端)
若要确认推送通知是否在移动环境中正常工作,请参考下方测试步骤。
-
GAMEPOT仪表盘:可在仪表盘 > 消息 > 推送通知菜单中确认是否发送推送。
-
Android(Firebase控制台)测试:在Firebase console > 选择项目 > Messaging > 活动上点击新活动按钮来发送测试消息。


输入在应用中收集到的推送Token后,点击测试按钮,即可确认该Token是否能够发送。
-
iOS(CloudKit Console)测试:在CloudKit Console > Push Notifications菜单中新建Notifications。

输入Device Token和Payload后确认发送结果。
解决问题
常见错误和解决方法如下。
Q. java.lang.IllegalStateException: Default FirebaseApp is not initialized in this process {包名称}. Make sure to call FirebaseApp.initializeApp(Context) first.
A. 当gradle文件中缺少推送设置时会发生此错误。请确认以下事项。
请确认项目顶部的build.gradle.kts。
- Gradle(项目根):
plugins {
id("com.google.gms.google-services") version "4.4.1" apply false
}
确认应用模块的build.gradle.kts。
- Gradle(模块:app):
plugins {
id("com.google.gms.google-services")
}
Q. org.gradle.api.GradleException: File google-services.json is missing. The Google Services Plugin cannot function without it.
A. 找不到google-services.json文件时会发生此错误。请确认该文件是否正确地放置在应用模块根文件中。(示例)./project folder/app/google-services.json