远程设置客户端¶
远程设置的 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
实例可用于获取远程设置记录。记录具有一些标准属性(id
、lastModified
等),还具有 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
}
}
getRecords
和 getRecordsMap
都输入一个可选的 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.getRecords
和 RemoteSettingsClient.getRecordsMap
永远不会抛出异常。如果遇到错误,它们将使用内部指标/错误报告记录它,然后返回 null
。这样做的原因是调用这些方法的代码肯定会像处理 null
一样处理异常,并且这样可以避免重复该代码。
使用计划下载防止空值¶
远程设置模块有一个系统,我们定期下载远程设置集合并将数据存储在库本身中。每当 getRecords
或 getRecordsMap
返回 null
时,此数据用作回退。这可以简化使用者,因为它不需要额外的分支来处理丢失的数据。这还可以减少网络流量,因为我们只需要在自上次下载以来记录已更新时才需要获取新记录。
如果您希望您的集合按此计划下载,请联系 DISCO 团队,我们可以进行设置。