管理 Firebase 安装

Firebase 安装服务 (FIS) 会为每个已安装的 Firebase 应用实例提供一个 Firebase 安装 ID (FID)。以下 Firebase 服务会在内部使用 Firebase 安装 ID:

Firebase 服务 Firebase 安装功能
Firebase Cloud Messaging

Firebase Cloud Messaging 使用 Firebase 安装 ID 来定位设备以实现消息传递。

Firebase Crashlytics

如果应用实例的 Firebase 安装 ID 发生改变,Firebase Crashlytics 会据此相应地轮替 Crashlytics 安装 UUID。将来,在启用崩溃报告和崩溃管理服务增强功能时,可能也会用到该安装 ID。

Firebase In-App Messaging

Firebase In-App Messaging 使用 Firebase 安装 ID 来定位设备以实现消息传递。

Firebase Performance Monitoring

Performance Monitoring 使用 Firebase 安装 ID 来计算访问网络资源的唯一 Firebase 安装的数量,以确保得出的访问模式 (access pattern) 具备充分的匿名性。它还将 Firebase 安装 ID 与 Firebase Remote Config 一起使用,以管理性能事件报告率。

Firebase Remote Config

Remote Config 使用 Firebase 安装 ID 选择要返回给最终用户设备的配置值。

Firebase ML

在与应用实例交互(例如向应用实例分发开发者模型)时,Firebase ML 使用名为安装身份验证令牌的凭据进行设备身份验证。

Firebase 用户细分存储

Firebase 用户细分存储功能可用来存储 Firebase 安装 ID 以及相关属性和细分信息,以便向使用这些 ID 及相关信息的其他 Firebase 服务提供定位信息。

通常,Firebase 安装服务的使用者是 Firebase 服务,开发者无需直接与 FIS API 交互。不过,在某些情况下,应用开发者可能希望直接调用 FIS API,例如:

  • 删除 Firebase 安装以及与之关联的数据。
  • 检索标识符(Firebase 安装 ID)以定位特定的应用安装。
  • 检索安装身份验证令牌以对 Firebase 安装进行身份验证。

如需开始直接调用 FIS API,请将相关 SDK 添加到您的应用中。

Firebase 安装 SDK 添加到您的应用中

iOS+

  1. Firebase 安装的依赖项添加到您的 Podfile 中:
    pod 'FirebaseInstallations'
  2. 运行 pod install 并打开创建的 .xcworkspace 文件。
  3. UIApplicationDelegate 中导入 FirebaseCore 模块,以及您的应用委托 (app delegate) 使用的所有其他 Firebase 模块。 例如,使用 Cloud FirestoreAuthentication

    SwiftUI

    import SwiftUI
    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Swift

    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Objective-C

    @import FirebaseCore;
    @import FirebaseFirestore;
    @import FirebaseAuth;
    // ...
          
  4. 在应用委托的 application(_:didFinishLaunchingWithOptions:) 方法中配置一个 FirebaseApp 共享实例:

    SwiftUI

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Swift

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    Objective-C

    // Use Firebase library to configure APIs
    [FIRApp configure];
  5. 如果您使用的是 SwiftUI,则必须创建应用委托并通过 UIApplicationDelegateAdaptorNSApplicationDelegateAdaptor 将其附加到 App 结构体。您还必须停用应用委托调配。如需了解详情,请参阅 SwiftUI 说明

    SwiftUI

    @main
    struct YourApp: App {
      // register app delegate for Firebase setup
      @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
      var body: some Scene {
        WindowGroup {
          NavigationView {
            ContentView()
          }
        }
      }
    }
          

Android

Firebase 安装 Android SDK 的依赖项添加到您的模块(应用级)Gradle 文件(通常为 app/build.gradle)中:

implementation 'com.google.firebase:firebase-installations:18.0.0'

JavaScript

您的配置可能会自动进行处理,或者您可能需要更新 Firebase 配置对象,具体取决于您的 Web 应用的托管方式。

例如,如果您将依赖项添加到 index.html 中,请在 <head> 元素中添加依赖项:

<script src="/__/firebase/11.0.2/firebase-installations.js"></script>

Flutter

  1. 从 Flutter 项目的根目录中运行以下命令,以安装 Firebase 安装插件:

    flutter pub add firebase_app_installations
    
  2. 重新构建您的项目:

    flutter run
    
  3. 导入 Firebase 安装插件:

    import 'package:firebase_app_installations/firebase_app_installations.dart';
    

删除 Firebase 安装

Firebase 安装关联的数据通常不属于可识别个人身份的信息。不过,为用户提供一个管理和删除此类数据的选项会很有帮助。

每个应用每次安装的 Firebase 安装 ID 都不同;同一设备上不同应用的 Firebase 安装 ID 也不同。Firebase 安装 ID 用于标识应用安装以及与这些应用安装关联的数据。

在您删除安装 ID 后,系统将在 180 天内从所有使用 Firebase 安装 ID 来标识安装的 Firebase 服务的实时系统和备份系统中移除与该安装 ID 关联的数据。Google 的删除和保留声明中概括地介绍了此过程。

除非您停用应用中的所有 FID 生成服务,否则 FIS 会在几天内创建新的 ID。Firebase 会将新创建的 ID 视为新的 Firebase 安装,不会以任何方式将其与之前的 ID 或数据相关联。

通过客户端 API 调用删除 FID

如需删除 Firebase 服务生成的 FID,请从 Firebase 安装 SDK 中调用相应的方法:

Swift

do {
  try await Installations.installations().delete()
  print("Installation deleted");
} catch {
  print("Error deleting installation: \(error)")
}

Objective-C

[[FIRInstallations installations] deleteWithCompletion:^(NSError *error) {
   if (error != nil) {
     NSLog(@"Error deleting Installation %@", error);
     return;
   }
   NSLog(@"Installation deleted");
}];

Java

FirebaseInstallations.getInstance().delete()
        .addOnCompleteListener(new OnCompleteListener<Void>() {
    @Override
    public void onComplete(@NonNull Task<Void> task) {
        if (task.isSuccessful()) {
            Log.d("Installations", "Installation deleted");
        } else {
            Log.e("Installations", "Unable to delete Installation");
        }
    }
});

Kotlin+KTX

FirebaseInstallations.getInstance().delete().addOnCompleteListener { task ->
    if (task.isComplete) {
        Log.d("Installations", "Installation deleted")
    } else {
        Log.e("Installations", "Unable to delete Installation")
    }
}

JavaScript

await firebase.installations().delete();

Dart

await FirebaseInstallations.instance.delete();

通过服务器 API 调用来删除 FID

如需通过服务器 API 调用来删除 FID,请将 Firebase Admin SDK 添加到您的服务器(如果尚未添加)。

添加 SDK 后,请以您选择的语言通过调用删除函数来删除 FID(请注意:除了 Node.js 之外,这些方法反映的是实例 ID 命名。但使用任何当前 Firebase SDK 进行调用时,这些方法实际上会删除 FID)。

Node.js

// An FIDsent from a client service SDK
const idToDelete = 'eyJhbGciOiJFUzI1N_iIs5';

admin.installations().deleteInstallation(idToDelete);

Java

// An FID sent from a client service SDK
String idToDelete = "eyJhbGciOiJFUzI1N_iIs5";

FirebaseInstanceId.getInstance().deleteInstanceIdAsync(idToDelete).get();

Python

  from firebase_admin import instance_id

  # An FID sent from a client service SDK
  id_to_delete = 'eyJhbGciOiJFUzI1N_iIs5'

  instance_id.delete_instance_id(id_to_delete)

Go

client, err := app.InstanceId(ctx)
if err != nil {
  log.Fatalln("error initializing client", err)
}

iidToDelete := "eyJhbGciOiJFUzI1N_iIs5"
if err := client.DeleteInstanceId(ctx, iidToDelete); err != nil {
  log.Fatalln("error deleting FID", err)
}

当您通过服务器 API 调用来删除 Firebase 安装 ID 后,Firebase 服务会开始删除与此安装 ID 关联的数据,并在 1-2 天内不再接受与该 ID 相关的数据,然后通知客户端应用此 ID 已被删除。在 Firebase 通知客户端应用之前,应用的某些服务可能仍会定位该 ID,例如,Firebase 安装可能会在几小时内继续接收 FCM 通知。

如果要删除当前的 Firebase 安装 ID 并立即将 Firebase 服务与新的不相关 ID 配合使用,请使用客户端 API 进行删除。

检索客户端标识符

如果您需要识别应用的特定安装,则可以通过检索 Firebase 安装 ID 来实现。例如,如需为 BigQuery 导入创建应用安装细分,或者在 Firebase In-App Messaging 开发期间执行测试,您可以使用相应的 Firebase 安装 ID 来识别和定位正确的设备。

如需检索 Firebase 安装 ID,请运行以下命令:

Swift

do {
  let id = try await Installations.installations().installationID()
  print("Installation ID: \(id)")
} catch {
  print("Error fetching id: \(error)")
}

Objective-C

[[FIRInstallations installations] installationIDWithCompletion:^(NSString *identifier, NSError *error) {
  if (error != nil) {
    NSLog(@"Error fetching Installation ID %@", error);
    return;
  }
  NSLog(@"Installation ID: %@", identifier);
}];

Java

FirebaseInstallations.getInstance().getId()
        .addOnCompleteListener(new OnCompleteListener<String>() {
    @Override
    public void onComplete(@NonNull Task<String> task) {
        if (task.isSuccessful()) {
            Log.d("Installations", "Installation ID: " + task.getResult());
        } else {
            Log.e("Installations", "Unable to get Installation ID");
        }
    }
});

Kotlin+KTX

FirebaseInstallations.getInstance().id.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        Log.d("Installations", "Installation ID: " + task.result)
    } else {
        Log.e("Installations", "Unable to get Installation ID")
    }
}

JavaScript

const installationId = await firebase.installations().getId();
console.log(installationId);

Dart

String id = await FirebaseInstallations.instance.getId();

检索安装身份验证令牌

Firebase 服务可以使用从 FIS 检索的身份验证令牌对 Firebase 安装进行身份验证。例如,在为 Remote Config 设计 A/B 测试时,您可以使用安装身份验证令牌对目标测试设备进行身份验证。

安装身份验证令牌是 JSON Web 令牌 (JWT) 格式的短期不记名令牌,其中包含以下安装信息:

  • Firebase 安装 ID
  • 关联的项目 (projectNumber)
  • 关联的 Firebase 应用 ID (appId)
  • 令牌的失效日期

安装身份验证令牌无法撤消,有效时间截至其失效日期。令牌的默认生命周期为一周。

如需检索安装身份验证令牌,请运行以下命令:

Swift

do {
  let result = try await Installations.installations()
    .authTokenForcingRefresh(true)
  print("Installation auth token: \(result.authToken)")
} catch {
  print("Error fetching token: \(error)")
}

Objective-C

[[FIRInstallations installations] authTokenForcingRefresh:true
                                               completion:^(FIRInstallationsAuthTokenResult *result, NSError *error) {
  if (error != nil) {
    NSLog(@"Error fetching Installation token %@", error);
    return;
  }
  NSLog(@"Installation auth token: %@", [result authToken]);
}];

Java

FirebaseInstallations.getInstance().getToken(/* forceRefresh */true)
        .addOnCompleteListener(new OnCompleteListener<InstallationTokenResult>() {
    @Override
    public void onComplete(@NonNull Task<InstallationTokenResult> task) {
        if (task.isSuccessful() && task.getResult() != null) {
            Log.d("Installations", "Installation auth token: " + task.getResult().getToken());
        } else {
            Log.e("Installations", "Unable to get Installation auth token");
        }
    }
});

Kotlin+KTX

val forceRefresh = true
FirebaseInstallations.getInstance().getToken(forceRefresh)
    .addOnCompleteListener { task ->
        if (task.isSuccessful) {
            Log.d("Installations", "Installation auth token: " + task.result?.token)
        } else {
            Log.e("Installations", "Unable to get Installation auth token")
        }
    }

JavaScript

const installationToken = await firebase.installations()
    .getToken(/* forceRefresh */ true);
console.log(installationToken);

Dart

String token = await FirebaseInstallations.instance.getToken();

监控 Firebase 安装 ID 生命周期

在应用正常运营期间,不需要特别对 Firebase 安装 ID (FID) 进行监控。但是,明确检索并使用 FID 的应用应添加逻辑,以监控 FID 的潜在删除或轮替。在下列情况下,FID 可能被删除或轮替:

  • 卸载或重新安装应用,例如最终用户在新设备上执行安装时。
  • 最终用户清除应用或设备的缓存。
  • 因应用处于非活跃状态而导致在后端触发 FID 删除操作(目前,触发此删除操作的阈值为处于非活跃状态 270 天)。

当应用在此类情况下遇到 FID 轮替或删除时,系统会为用户分配新的 FID。此外,与已删除的 FID 关联的安装身份验证令牌将被删除,无论其自身成熟度如何,此令牌都会替换为新的安装身份验证令牌。

应用可以监控这些更改,并相应地做出响应。

如需监控 FID 轮替,请执行以下操作:

Swift

installationIDObserver = NotificationCenter.default.addObserver(
        forName: .InstallationIDDidChange,
        object: nil,
        queue: nil
) { (notification) in
  // Fetch new Installation ID
  Task {
    await self.fetchInstallationToken()
  }
}

Objective-C

__weak __auto_type weakSelf = self;
self.installationIDObserver = [[NSNotificationCenter defaultCenter]
        addObserverForName: FIRInstallationIDDidChangeNotification
                    object:nil
                     queue:nil
                usingBlock:^(NSNotification * _Nonnull notification) {
    // Fetch new Installation ID
    [weakSelf fetchInstallationsID];
}];

每当分配新的 FID 时,名为 NSNotificationName.InstallationIDDidChange 的 NSNotification 都会发布到默认 NSNotificationCenter。

Android

Kotlin 和 Java 客户端应添加重试逻辑,对失败的调用进行响应,以检索新的 FID。

JavaScript

Web 应用可以订阅 onIdChange 钩子。

每次创建新的 FID 时,都会触发订阅回调:

await firebase.installations().onIdChange((newId) => {
  console.log(newId);
  // TODO: Handle new installation ID.
});

Dart

FirebaseInstallations.instance.onIdChange.listen((token) {
  print('FID token: $token');
});

从实例 ID 迁移到 Firebase 安装

在引入 Firebase 安装之前,Firebase 依赖实例 ID SDK 获取应用安装的标识符。Firebase 安装在可靠性、性能和安全性方面远胜于实例 ID。依赖实例 ID SDK 的 Firebase 应用应迁移到 Firebase 安装。

迁移流程因应用而异:

  • 不直接调用实例 ID API 的应用可以通过更新其 SDK 版本进行迁移。大多数 Firebase 应用都属于这一类别。

  • 明确对实例 ID 进行 API 调用的应用必须更新 SDK 版本,更改代码,将实例 ID 方法替换为其 Firebase 安装或 FCM 等效方法。如果您的应用使用实例 ID 来检索 FCM 注册令牌,或者明确使用实例 ID 来定位应用实例或实现任何其他目的,则您需要更新应用代码。

目前,FIS 向后兼容旧版标识符 Firebase 实例 ID。删除 IID 是使用这些 Firebase SDK 来请求删除数据的另一种方法:

  • iOS 6.14.0 及更低版本
  • 早于 2020 年 2 月 27 日的 Android SDK

这意味着应用不需要迁移到 Firebase 安装,但我们强烈建议进行迁移。

升级到 Firebase 安装 SDK 最低版本

如需从实例 ID 迁移到 Firebase 安装,请确保您的应用至少使用的是以下列出的 Firebase SDK 的最低版本:

Firebase SDK Android 最低版本 iOS 最低版本
Firebase Cloud Messaging v20.3.0 v6.34.0
Remote Config v19.2.0 v6.24.0
Google Analytics for Firebase \ (衡量 SDK) v17.4.4 v6.18.0
In-App Messaging v19.0.7 v6.24.0
Performance Monitoring v19.0.8 v6.21.0
Crashlytics v17.2.1 v6.23.0
机器学习套件 v22.1.2 v6.28.0

更新明确调用 Instance ID API 的代码

如果您的 Android 或 Apple 应用直接使用实例 ID SDK 方法,则可以将该用法替换为 Firebase 安装 SDK 或 FCM SDK 中的相同替代方法。

检索标识符

将获取实例 ID 的方法替换为获取安装 ID 的方法。例如:

之前

Swift

Messaging.messaging().token { token, error in
  if let error = error {
    print("Error fetching remote FCM registration token: \(error)")
  } else if let token = token {
    print("Remote instance ID token: \(token)")
    self.remoteFCMTokenMessage.text = "Remote FCM registration token: \(token)"
  }
}

Objective-C

[[FIRMessaging messaging] tokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
   if (error != nil) {
     NSLog(@"Error fetching the remote FCM registration token: %@", error);
   } else {
     NSLog(@"Remote FCM registration token: %@", token);
     NSString* message =
       [NSString stringWithFormat:@"FCM registration token: %@", token];
     self.remoteFCMTokenMessage.text = message;
   }
 }];

Java

FirebaseInstanceId.getInstance().getInstanceId()
        .addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
            @Override
            public void onComplete(@NonNull Task<InstanceIdResult> task) {
                Log.d("IID_TOKEN", task.getResult().getToken());
            }
        });

Kotlin+KTX

FirebaseInstanceId.getInstance().instanceId
        .addOnSuccessListener { result ->
            Log.d("IID_TOKEN", result.token)
        }

之后

Swift

do {
  let id = try await Installations.installations().installationID()
  print("Installation ID: \(id)")
} catch {
  print("Error fetching id: \(error)")
}

Objective-C

[[FIRInstallations installations] installationIDWithCompletion:^(NSString *identifier, NSError *error) {
  if (error != nil) {
    NSLog(@"Error fetching Installation ID %@", error);
    return;
  }
  NSLog(@"Installation ID: %@", identifier);
}];

Java

FirebaseInstallations.getInstance().getId()
        .addOnCompleteListener(new OnCompleteListener<String>() {
    @Override
    public void onComplete(@NonNull Task<String> task) {
        if (task.isSuccessful()) {
            Log.d("Installations", "Installation ID: " + task.getResult());
        } else {
            Log.e("Installations", "Unable to get Installation ID");
        }
    }
});

Kotlin+KTX

FirebaseInstallations.getInstance().id.addOnCompleteListener { task ->
    if (task.isSuccessful) {
        Log.d("Installations", "Installation ID: " + task.result)
    } else {
        Log.e("Installations", "Unable to get Installation ID")
    }
}

删除标识符

将删除实例 ID 的方法替换为删除 Firebase 安装 ID 的方法。例如:

之前

Swift

InstanceID.instanceID().deleteID { error in
  if let error = error {
    print("Error deleting instance ID: \(error)")
  }
}

Objective-C

[FIRInstanceID instanceID] deleteIDWithHandler:^(NSError *error) {
  if error != nil {
    NSLog(@"Error deleting instance ID: %@", error);
  }
}];

Android

FirebaseInstanceId.deleteInstanceId();

之后

Swift

func delete(completion: @escaping (Error?) -> Void)

Objective-C

- (void)deleteWithCompletion:(nonnull void (^)(NSError *_Nullable))completion;

Java

FirebaseInstallations.getInstance().delete()
        .addOnCompleteListener(new OnCompleteListener<Void>() {
    @Override
    public void onComplete(@NonNull Task<Void> task) {
        if (task.isSuccessful()) {
            Log.d("Installations", "Installation deleted");
        } else {
            Log.e("Installations", "Unable to delete Installation");
        }
    }
});

Kotlin+KTX

FirebaseInstallations.getInstance().delete().addOnCompleteListener { task ->
    if (task.isComplete) {
        Log.d("Installations", "Installation deleted")
    } else {
        Log.e("Installations", "Unable to delete Installation")
    }
}

检索 FCM 注册令牌

在引入 Firebase 安装之前,FCM 客户端会从实例 ID 中检索注册令牌。现在,FCM SDK 提供了用于检索注册令牌的方法。

之前

Java

FirebaseInstanceId.getInstance().getInstanceId()
        .addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
            @Override
            public void onComplete(@NonNull Task<InstanceIdResult> task) {
                if (!task.isSuccessful()) {
                    Log.w(TAG, "getInstanceId failed", task.getException());
                    return;
                }

                // Get new Instance ID token
                String token = task.getResult().getToken();

                // Log and toast
                String msg = getString(R.string.msg_token_fmt, token);
                Log.d(TAG, msg);
                Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });

Kotlin+KTX

FirebaseInstanceId.getInstance().instanceId
        .addOnCompleteListener(OnCompleteListener { task ->
            if (!task.isSuccessful) {
                Log.w(TAG, "getInstanceId failed", task.exception)
                return@OnCompleteListener
            }

            // Get new Instance ID token
            val token = task.result?.token

            // Log and toast
            val msg = getString(R.string.msg_token_fmt, token)
            Log.d(TAG, msg)
            Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
        })

Swift

Messaging.messaging().token { token, error in
  if let error = error {
    print("Error fetching remote FCM registration token: \(error)")
  } else if let token = token {
    print("Remote instance ID token: \(token)")
    self.remoteFCMTokenMessage.text = "Remote FCM registration token: \(token)"
  }
}

Objective-C

[[FIRMessaging messaging] tokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
   if (error != nil) {
     NSLog(@"Error fetching the remote FCM registration token: %@", error);
   } else {
     NSLog(@"Remote FCM registration token: %@", token);
     NSString* message =
       [NSString stringWithFormat:@"FCM registration token: %@", token];
     self.remoteFCMTokenMessage.text = message;
   }
 }];

之后

Java

FirebaseMessaging.getInstance().getToken()
    .addOnCompleteListener(new OnCompleteListener<String>() {
        @Override
        public void onComplete(@NonNull Task<String> task) {
          if (!task.isSuccessful()) {
            Log.w(TAG, "Fetching FCM registration token failed", task.getException());
            return;
          }

          // Get new FCM registration token
          String token = task.getResult();

          // Log and toast
          String msg = getString(R.string.msg_token_fmt, token);
          Log.d(TAG, msg);
          Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
        }
    });

Kotlin+KTX

FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
    if (!task.isSuccessful) {
        Log.w(TAG, "Fetching FCM registration token failed", task.exception)
        return@OnCompleteListener
    }

    // Get new FCM registration token
    val token = task.result

    // Log and toast
    val msg = getString(R.string.msg_token_fmt, token)
    Log.d(TAG, msg)
    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
})

Swift

Messaging.messaging().token { token, error in
  if let error = error {
    print("Error fetching FCM registration token: \(error)")
  } else if let token = token {
    print("FCM registration token: \(token)")
    self.fcmRegTokenMessage.text  = "Remote FCM registration token: \(token)"
  }
}

Objective-C

[[FIRMessaging messaging] tokenWithCompletion:^(NSString *token, NSError *error) {
  if (error != nil) {
    NSLog(@"Error getting FCM registration token: %@", error);
  } else {
    NSLog(@"FCM registration token: %@", token);
    self.fcmRegTokenMessage.text = token;
  }
}];