Catch up on everthing we announced at this year's Firebase Summit. Learn more

实时传播 Remote Config 更新动态

Cloud Functions for Firebase 提供的 Remote Config 后台函数触发功能与 FCM 结合使用,您可以实时传播 Remote Config 更新动态。在此情形下,您需要创建一个函数,使其在您通过信息中心或 API 发布或回滚远程配置模板时触发。模板更新会触发此函数发送 FCM 消息,告知客户端它们当前的配置已过时,下次应从服务器提取配置:

显示通过 Cloud Functions 触发 FCM 提醒的 Remote Config 更新的图表

本文档的其余部分将逐步介绍实时传播远程配置更新动态的操作。

为客户端应用实例订阅 FCM 主题

要向大量客户端应用实例(例如整个用户群)发送 FCM 消息,主题消息传递是最有效的机制。 需要接收实时远程配置更新动态的每个应用实例都必须订阅一个主题名称,例如 PUSH_RC

Swift

extension AppDelegate : MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
        messaging.subscribe(toTopic: "PUSH_RC") { error in
            print("Subscribed to PUSH_RC topic")
        }
    }
}
    

Objective-C

- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
    [[FIRMessaging messaging] subscribeToTopic:@"PUSH_RC" completion:^(NSError * _Nullable error) {
        NSLog(@"Subscribed to PUSH_RC topic");
    }];
}
    

Android

@Override
public void onNewToken(String s) {
    FirebaseMessaging.getInstance().subscribeToTopic("PUSH_RC");
}
    

创建一个函数以在模板更新时发送 FCM ping

您可以触发一个函数来响应远程配置事件(包括发布新的配置版本或回滚到旧版本)。 要实时传播模板更新动态,请创建一个函数来侦听模板发布事件,然后在函数中使用 FCM Admin SDK 向客户端应用实例发送静默 ping:

exports.pushConfig = functions.remoteConfig.onUpdate(versionMetadata => {
  // Create FCM payload to send data message to PUSH_RC topic.
  const payload = {
    topic: "PUSH_RC",
    data: {
      "CONFIG_STATE": "STALE"
    }
  };
  // Use the Admin SDK to send the ping via FCM.
  return admin.messaging().send(payload).then(resp => {
    console.log(resp);
    return null;
  });
});

此函数会设置 CONFIG_STATE 参数,然后将其作为 FCM 消息的数据载荷发送给所有订阅了 PUSH_RC 主题的客户端。

在客户端上设置远程配置状态

在应用的共享偏好设置中,上一步中所示的数据载荷始终会将 CONFIG_STATE 设置为 STALE。它表示由于创建了更新后的新模板(该模板的发布触发了相应函数),之前存储在应用上的远程配置模板已经过时。请更新您的通知处理程序并针对这种状况进行测试:

Swift

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

    if (userInfo.index(forKey: "CONFIG_STATE") != nil) {
        print("Config set to stale")
        UserDefaults.standard.set(true, forKey:"CONFIG_STALE")
    }

    completionHandler(UIBackgroundFetchResult.newData)
}
    

Objective-C

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

    if (userInfo[@"CONFIG_STATE"]) {
        NSLog(@"Config set to stale");
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"CONFIG_STALE"];
    }

    completionHandler(UIBackgroundFetchResultNewData);
}
    

Android

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    if (remoteMessage.getData().containsKey("CONFIG_STATE")) {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        sharedPreferences.edit().putBoolean("CONFIG_STALE", true).apply();
    }
}
    

在应用启动时提取远程配置更新

Swift

func fetchConfig() {
  welcomeLabel.text = remoteConfig[loadingPhraseConfigKey].stringValue

  var expirationDuration = 3600
  // If your app is using developer mode, expirationDuration is set to 0, so each fetch will
  // retrieve values from the service.
  if remoteConfig.configSettings.isDeveloperModeEnabled || UserDefaults.standard.bool(forKey: "CONFIG_STALE") {
    expirationDuration = 0
  }

  remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
    if status == .success {
      print("Config fetched!")
      self.remoteConfig.activateFetched()
    } else {
      print("Config not fetched")
      print("Error: \(error?.localizedDescription ?? "No error available.")")
    }
    self.displayWelcome()
  }
}
    

Objective-C

- (void)fetchConfig {
    self.welcomeLabel.text = self.remoteConfig[kLoadingPhraseConfigKey].stringValue;

    long expirationDuration = 3600;
    // If your app is using developer mode, expirationDuration is set to 0, so each fetch will
    // retrieve values from the Remote Config service.
    if (self.remoteConfig.configSettings.isDeveloperModeEnabled || [[NSUserDefaults standardUserDefaults] boolForKey:@"CONFIG_STALE"]) {
        expirationDuration = 0;
    }

    [self.remoteConfig fetchWithExpirationDuration:expirationDuration completionHandler:^(FIRRemoteConfigFetchStatus status, NSError *error) {
        if (status == FIRRemoteConfigFetchStatusSuccess) {
            NSLog(@"Config fetched!");
            [self.remoteConfig activateFetched];
            [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"CONFIG_STALE"];
        } else {
            NSLog(@"Config not fetched");
            NSLog(@"Error %@", error.localizedDescription);
        }
        [self displayWelcome];
    }];
}
    

Android

private void fetchWelcomeMessage() {
    mWelcomeTextView.setText(mFirebaseRemoteConfig.getString("loading_phrase"));

    long cacheExpiration = 43200; // 12 hours in seconds.
    // If your app is using developer mode or cache is stale, cacheExpiration is set to 0,
    // so each fetch will retrieve values from the service.
    if (mFirebaseRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled() ||
            mSharedPreferences.getBoolean("CONFIG_STALE", false)) {
        cacheExpiration = 0;
    }

    mFirebaseRemoteConfig.fetch(cacheExpiration)
            .addOnCompleteListener(this, new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if (task.isSuccessful()) {
                        Toast.makeText(MainActivity.this, "Fetch Succeeded",
                                Toast.LENGTH_SHORT).show();

                        // After config data is successfully fetched, it must be activated before newly fetched
                        // values are returned.
                        mFirebaseRemoteConfig.activateFetched();
                    } else {
                        Toast.makeText(MainActivity.this, "Fetch Failed",
                                Toast.LENGTH_SHORT).show();
                    }
                    mWelcomeTextView.setText(mFirebaseRemoteConfig.getString("welcome_message"));
                }
            });
}
    

最后,在您的应用中添加逻辑以在 CONFIG_STATESTALE 时强制从网络中提取 Remote Config(忽略本地存储空间)。如果您的应用过于频繁地从网络中进行提取,则可能会受到 Firebase 的限制。请参阅限制