远程设置客户端

远程设置的 API 可以在 Mozilla Rust 组件的 Kotlin API 参考Swift API 参考 中找到。

注意

确保为该组件初始化 Viaduct

警告

远程设置代码是同步的,这意味着它需要包装在您正在使用的目标语言的异步原语中。

导入项目

import mozilla.appservices.remotesettings.RemoteSettingsClient
import mozilla.appservices.remotesettings.RemoteSettingsConfig2
import mozilla.appservices.remotesettings.RemoteSettingsException
import mozilla.appservices.remotesettings.RemoteSettingsServer
import mozilla.appservices.remotesettings.RemoteSettingsService
import MozillaAppServices

应用程序级设置

应用程序应该创建一个应用程序范围的 RemoteSettingsService。它管理向哪个远程设置服务器发出请求,与该服务器同步数据等。

RemoteSettingsService 实例是使用配置创建的。名称是因为有一个较旧/半弃用的 RemoteSettingsConfig 类。DISCO 团队计划重命名此类并删除 2,一旦我们将使用者迁移到新的 API。

val config = RemoteSettingsConfig2(
   // Remote Settings server to connect to.  Other options are:
   //  * RemoteSettingsServer.Stage()
   //  * RemoteSettingsServer.Dev()
   //  * RemoteSettingsServer.Custom(url)
   server = RemoteSettingsServer.Prod(),
   storageDir = pathToDir,
   // optional field to fetch from a non-standard bucket
   bucketName = if (usePreviewBucket) { "main-preview" } else { "main" }
)

val appWideRemoteSettingsService = RemoteSettingsService(config)
let config = RemoteSettingsConfig2(
   // Remote Settings server to connect to.  Other options are:
   //  * RemoteSettingsServer.stage
   //  * RemoteSettingsServer.dev
   //  * RemoteSettingsServer.custom(url: url)
   server = RemoteSettingsServer.prod,
   storageDir = pathToDir,
   // optional field to fetch from a non-standard bucket
   bucketName = if usePreviewBucket { "main-preview" } else { "main" }
)

let appWideRemoteSettingsService = RemoteSettingsService(config: config)

创建远程设置客户端

RemoteSettingsService 实例可用于创建新的 RemoteSettingsClient 实例,这些实例为特定集合获取远程设置记录。

val remoteSettingsClient = appWideRemoteSettingsService.makeClient("my-collection")
let remoteSettingsClient = appWideRemoteSettingsService.makeClient(collection: "my-collection")

获取记录

RemoteSettingsClient 实例可用于获取远程设置记录。记录具有一些标准属性(idlastModified 等),还具有 fields 属性,该属性存储所有其他 JSON 数据,这些数据作为字符串序列化。

getRecords 不会发出网络请求,而是返回与服务器同步的最后数据。这使得它可以在早期启动时安全地调用,在早期启动时启动新的网络请求是不希望的。但是,这意味着它返回一个可为空的值,必须对其进行检查。

fun processRecords(remoteSettingsClient: RemoteSettingsClient) {
    val records = remoteSettingsClient.getRecords()
    if (records != null) {
        for (record in records) {
            processRecord(record.id, deserialize(record.field))
        }
    }
}
func processRecords(remoteSettingsClient: RemoteSettingsClient) {
    let records = remoteSettingsClient.getRecords()
    if let records = records {
        for record in records {
            processRecord(id: record.id, recordData: deserialize(record.field))
        }
    }
}

getRecordsMap 的工作方式类似,但它返回一个以记录 ID 作为键的映射。同样,此值是可为空的,因为客户端可能尚未同步任何数据。

fun valueOfFeature(remoteSettingsClient: RemoteSettingsClient): Boolean {
    val records = remoteSettingsClient.getRecordsMap()
    if (records != null) {
        return deserializeFeatureValue(records["featureName"].field)
    } else {
        return DEFAULT_FEATURE_VALUE
    }
}
func valueOfFeature(remoteSettingsClient: RemoteSettingsClient): Bool {
    let records = remoteSettingsClient.getRecordsMap()
    if let records = records {
        return deserializeFeatureValue(recordData: records["featureName"].field)
    } else {
        return DEFAULT_FEATURE_VALUE
    }
}

getRecordsgetRecordsMap 都输入一个可选的 syncIfEmpty 参数。传递 syncIfEmpty=true 以在以前未同步记录的情况下与服务器同步记录。即使使用此参数,您也应始终检查空值,如果网络请求失败,则将返回空值。 syncIfEmpty 应谨慎使用,因为获取设置可能会延迟。例如,它可能会延迟 UI 更新。

获取附件数据

RemoteSettingsRecord 实例具有可选的附件字段。如果存在,您可以使用 RemoteSettingsClient.getAttachment 将附件数据下载为字节数组。这将发出网络请求,除非附件数据已缓存。

    val records = remoteSettingsClient.getRecords()
    if (records.size > 0 && records[0].attachment != null) {
        val attachmentData: ByteArray = remoteSettingsClient.getAttachment(records[0].attachment.location)
        // do something with the attachment data
    }
}
    let records = remoteSettingsClient.getRecords()
    if (records.count > 0 && records[0].attachment != nil) {
        val attachmentData: Data = remoteSettingsClient.getAttachment(location: records[0].attachment.location)
        // do something with the attachment data
    }

与服务器同步

使用 RemoteSettingsService.sync() 将远程设置数据与服务器同步。这将获取使用仍在活动状态的 RemoteSettingsService 创建的所有客户端的远程设置数据。此同步可能需要大量时间,因此最好在工作队列中运行。

异常处理

远程设置组件定义以下错误层次结构

  • **RemoteSettingsError**: 基本错误

    • **RemoteSettingsError.Network(reason: string)**: 发出请求时发生的网络错误

    • **RemoteSettingsError.Backoff(seconds: int)**: 服务器请求至少 [秒] 的请求回退

    • **RemoteSettingsError.Other(reason: string)**: 其他远程设置错误的总括

工作原理取决于语言

fun remoteSettingsPeriodicSync() {
    // On Kotlin, errors are sealed/nested classes.
    // "Error" is replaced with "Exception" for consistency with other exceptions.
    try {
        appWideRemoteSettingsService.sync()
    } catch (e: RemoteSettingsException.Network) {
        // Try again after 5 minutes
        Log.w("Network error when syncing Remote Settings: ${e.reason}")
        scheduleRemoteSettingsSyncAfter(300)
    } catch (e: RemoteSettingsException.Backoff) {
        Log.w("Backoff error when syncing Remote Settings")
        scheduleRemoteSettingsSyncAfter(e.seconds)
    } catch (e: RemoteSettingsException.Other) {
        // There's no reason to think another sync will work.
        // Sync again using the normal schedule
        Log.w("Unexpected error when syncing Remote Settings: ${e.reason}")
    }
}
func remoteSettingsPeriodicSync() {
    // On Swift errors are members of the base error enum.
    do {
        appWideRemoteSettingsService.sync()
    } catch RemoteSettingsError.Network(let reason) {
        // Try again after 5 minutes
        print("Network error when syncing Remote Settings: \(reason)")
        scheduleRemoteSettingsSyncAfter(seconds: 300)
    } catch RemoteSettingsError.Backoff(let seconds) {
        print("Backoff error when syncing Remote Settings")
        scheduleRemoteSettingsSyncAfter(seconds: seconds)
    } catch RemoteSettingsError.Other(let reason) {
        // There's no reason to think another sync will work.
        // Sync again using the normal schedule
        print("Unexpected error when syncing Remote Settings: \(reason)")
    }
}

RemoteSettingsClient.getRecordsRemoteSettingsClient.getRecordsMap 永远不会抛出异常。如果遇到错误,它们将使用内部指标/错误报告记录它,然后返回 null。这样做的原因是调用这些方法的代码肯定会像处理 null 一样处理异常,并且这样可以避免重复该代码。

使用计划下载防止空值

远程设置模块有一个系统,我们定期下载远程设置集合并将数据存储在库本身中。每当 getRecordsgetRecordsMap 返回 null 时,此数据用作回退。这可以简化使用者,因为它不需要额外的分支来处理丢失的数据。这还可以减少网络流量,因为我们只需要在自上次下载以来记录已更新时才需要获取新记录。

如果您希望您的集合按此计划下载,请联系 DISCO 团队,我们可以进行设置。