1.简介
您可以使用 Firebase Remote Config 在应用中定义键值对(也称为参数),并在云端更新它们的值。这样,您无需更新应用即可修改应用的外观和行为。
您要将此新功能添加到示例游戏 MechaHamster: Level Up with Firebase Edition 中。此示例游戏是经典 Firebase 游戏 MechaHamster 的新版本,该版本移除了其大部分内置 Firebase 功能,让您有机会取代 Firebase 的新用途。
为确保应用按预期运行,您将为示例游戏代码中的值设置默认配置,并且这些值可以替换为您在 Firebase 控制台的 Remote Config 中设置的值。
学习内容
- 如何在云端设置并检索 Remote Config 值
- 如何对 Unity C# 代码进行插桩,以自动使用检索的值
- 如何将复合值/对象存储、插桩和替换为 JSON 值
- 如何使用 Remote Config 条件向不同的用户组提供不同的值变体
您需要满足的条件
- 支持 iOS 和/或 Android 构建的 Unity 2019.1.0f1 或更高版本
- 一台 Android/iOS 实体设备或一个用于构建和运行游戏的模拟器/模拟器
2. 设置您的开发环境
以下部分介绍了如何下载 Level Up with Firebase 代码,在 Unity 中打开该代码,并添加 Firebase 项目。其他几个 Firebase + Unity Codelab 也在使用以下《Level Up with Firebase》示例游戏,因此您可能已经完成本部分中的任务。如果是这样,您可以跳过这些步骤,继续“添加 Firebase SDK for Unity”,将 Remote Config 添加到示例游戏代码中。
下载代码
从命令行克隆此 Codelab 的 GitHub 代码库:
git clone https://github.com/firebase/level-up-with-firebase
或者,如果您尚未安装 git,可以下载 ZIP 文件形式的代码库。
在 Unity 编辑器中打开 Level Up with Firebase
- 启动 Unity Hub,然后在 Projects 标签页中点击 Open 旁边的下拉箭头。
- 点击从磁盘添加项目。
- 转到包含代码的目录,然后点击 OK。
- 如果出现提示,请选择要使用的 Unity 编辑器版本和您的目标平台(Android 或 iOS)。
- 点击项目名称 level-up-with-firebase,项目将在 Unity 编辑器中打开。
- 如果您的编辑器未自动打开它,请在 Assets 中打开
MainGameScene
>Unity 编辑器的 Project 标签页中的 Hamster。
如需详细了解如何安装和使用 Unity,请参阅在 Unity 中工作。
3. 将 Firebase 添加到您的 Unity 项目
创建 Firebase 项目
- 在 Firebase 控制台中,点击添加项目。
- 如需创建新项目,请输入要使用的项目名称。
此操作还会根据项目名称将项目 ID(显示在项目名称下方)设置为一个特定的值。您可以选择性地点击项目 ID 上的修改图标,进一步对其进行自定义。 - 如果看到相关提示,请查看并接受 Firebase 条款。
- 点击继续。
- 选择为此项目启用 Google Analytics 选项,然后点击继续。
- 选择要使用的现有 Google Analytics 账号,或选择创建新账号来创建新账号。
- 点击 Create project。
- 创建项目后,点击继续。
在 Firebase 中注册您的应用
- 打开 Firebase 控制台,在项目概览页面的中心位置点击 Unity 图标以启动设置工作流;如果您已向 Firebase 项目添加了应用,请点击添加应用以显示平台选项。
- 选择同时注册 Apple (iOS) 和 Android build 目标。
- 输入 Unity 项目的平台专用 ID。对于此 Codelab,请输入以下内容:
- 对于 Apple (iOS) - 在 iOS 软件包 ID 字段中输入
com.google.firebase.level-up
。 - 对于 Android - 在 Android 软件包名称字段中输入
com.google.firebase.level_up
。
- 对于 Apple (iOS) - 在 iOS 软件包 ID 字段中输入
- (可选)输入 Unity 项目针对具体平台的别名。
- 点击注册应用,然后继续下载配置文件部分。
- 对您第一次没有执行的构建目标重复此过程。
添加 Firebase 配置文件
点击 Register app(注册应用)后,系统会提示您下载两个配置文件(每个构建目标对应一个配置文件)。您的 Unity 项目需要这些文件中的 Firebase 元数据才能与 Firebase 关联。
- 下载两个可用的配置文件:
- 对于 Apple (iOS):下载 GoogleService-Info.plist。
- 对于 Android 应用:下载 google-services.json。
- 打开 Unity 项目的 Project 窗口,然后将两个配置文件都移到 Assets 文件夹中。<ph type="x-smartling-placeholder">
- 返回 Firebase 控制台,在设置工作流中,点击下一步,然后继续“添加 Firebase SDK for Unity”。
注意:您以后可以随时重新下载这些文件,方法是打开项目的常规设置,向下滚动到您的应用部分,然后点击所需配置文件的下载按钮。
添加适用于 Unity 的 Firebase SDK
- 在 Firebase 控制台中点击下载 Firebase Unity SDK。
- 将 SDK 解压缩到方便的位置。
- 在您打开的 Unity 项目中,前往 Assets >导入软件包 >自定义软件包。
- 在 Import package 对话框中,转到包含解压缩的 SDK 的目录,选择
FirebaseAnalytics.unitypackage
,然后点击 Open。 - 在随即显示的 Import Unity Package 对话框中,点击 Import。
- 重复前面的步骤,导入以下两个软件包:
FirebaseRemoteConfig.unitypackage
FirebaseCrashlytics.unitypackage
Crashlytics 是一个轻量级的实时崩溃报告解决方案,可帮助您对影响应用质量的稳定性问题进行跟踪、确定优先解决顺序并加以修复。如果您之前未使用过该工具,建议您完成适用于 Unity 的 Crashlytics 学习开发者在线课程。
- 返回 Firebase 控制台,然后在设置工作流中点击下一步。
如需详细了解如何将 Firebase SDK 添加到 Unity 项目,请参阅其他 Unity 安装选项。
4. 设置 Remote Config 默认值并提取新值
在此 Codelab 中,您将更新使用代码中定义的值或在 Unity 编辑器中序列化的值的对象,以使用通过 Remote Config 进行插桩的值。您将使用 SetDefaultsAsync
为每个参数配置默认值,以使应用在连接到 Remote Config 后端之前能够按预期运行。通过从 Remote Config 提取新值并激活这些值以使其可在代码中使用,您的应用将保持最新状态。
如需从 Remote Config 中提取新值,必须完成 Assets/Hamster/Scripts/MainGame.cs
文件中已存在许多未实现的方法。
- 将以下
using
语句添加到MainGame.cs
:using Firebase.Crashlytics; using Firebase.Extensions; using Firebase.RemoteConfig;
Firebase.Extensions
模块包含 C# Tasks API 的一些扩展,有助于简化通过回调管理初始化过程。 - 将现有的 InitializeCommonDataAndStartGame() 方法替换为当前未实现的方法
InitializeFirebaseAndStartGame()
,从而在MainGame.cs
Start()
方法中添加 Firebase 初始化:void Start() { Screen.SetResolution(Screen.width / 2, Screen.height / 2, true); InitializeFirebaseAndStartGame(); }
- 在
MainGame.cs
中,找到InitializeFirebaseAndStartGame()
。声明一个应用变量并覆盖该方法的实现,如下所示:public Firebase.FirebaseApp app = null; // Begins the firebase initialization process and afterwards, opens the main menu. private void InitializeFirebaseAndStartGame() { Firebase.FirebaseApp.CheckAndFixDependenciesAsync() .ContinueWithOnMainThread( previousTask => { var dependencyStatus = previousTask.Result; if (dependencyStatus == Firebase.DependencyStatus.Available) { // Create and hold a reference to your FirebaseApp, app = Firebase.FirebaseApp.DefaultInstance; // Set the recommended Crashlytics uncaught exception behavior. Crashlytics.ReportUncaughtExceptionsAsFatal = true; SetRemoteConfigDefaults(); } else { UnityEngine.Debug.LogError( $"Could not resolve all Firebase dependencies: {dependencyStatus}\n" + "Firebase Unity SDK is not safe to use here"); } }); }
- Firebase 初始化会在成功时调用
SetRemoteConfigDefaults
以设置应用内默认值。将未实现的 SetRemoteConfigDefaults 方法替换为以下代码:private void SetRemoteConfigDefaults() { var defaults = new System.Collections.Generic.Dictionary < string, object > (); defaults.Add( Hamster.MapObjects.AccelerationTile.AccelerationTileForceKey, Hamster.MapObjects.AccelerationTile.AccelerationTileForceDefault); defaults.Add( Hamster.States.MainMenu.SubtitleOverrideKey, Hamster.States.MainMenu.SubtitleOverrideDefault); var remoteConfig = FirebaseRemoteConfig.DefaultInstance; remoteConfig.SetDefaultsAsync(defaults).ContinueWithOnMainThread( previousTask => { FetchRemoteConfig(InitializeCommonDataAndStartGame); } ); }
5. 提取并激活新值(根据需要)
现在,我们需要完成现有的 FetchRemoteConfig
方法。这将使用名为 onFetchAndActivateSuccessful
的回调参数链接对 Remote Config 方法 FetchAsync
(用于从 Remote Config 提取新值)和 ActivateAsync
(用于激活这些获取的值,以使其在代码中可用)的调用。
我们在上一步中添加的启动代码使用 InitializeCommonDataAndStartGame
作为回调来调用 FetchRemoteConfig
,以便在序列结束时启动游戏。您可以将备用回调传递给 FetchRemoteConfig
,以便以不同的结果调用提取。一个示例(您将在稍后实现)是传入一个方法,用于打开新的界面菜单,而该菜单依赖于 Remote Config 值。这会导致菜单只有在提取并激活这些值后才会打开。
- 将以下代码粘贴到
FetchRemoteConfig
中:
<ph type="x-smartling-placeholder">public void FetchRemoteConfig(System.Action onFetchAndActivateSuccessful) { if(app==null) { Debug.LogError($"Do not use Firebase until it is properly initialized by calling {nameof(InitializeFirebaseAndStartGame)}."); return; } Debug.Log("Fetching data..."); var remoteConfig = FirebaseRemoteConfig.DefaultInstance; remoteConfig.FetchAsync(System.TimeSpan.Zero).ContinueWithOnMainThread( previousTask=> { if (!previousTask.IsCompleted) { Debug.LogError($"{nameof(remoteConfig.FetchAsync)} incomplete: Status '{previousTask.Status}'"); return; } ActivateRetrievedRemoteConfigValues(onFetchAndActivateSuccessful); }); }
- 接下来,完成
ActivateRetrievedRemoteConfigValues
方法,该方法将收到传入的回调onFetchAndActivateSuccessful
。激活完成后,将调用指定的回调:private void ActivateRetrievedRemoteConfigValues(System.Action onFetchAndActivateSuccessful) { var remoteConfig = FirebaseRemoteConfig.DefaultInstance; var info = remoteConfig.Info; if(info.LastFetchStatus == LastFetchStatus.Success) { remoteConfig.ActivateAsync().ContinueWithOnMainThread( previousTask => { Debug.Log($"Remote data loaded and ready (last fetch time {info.FetchTime})."); onFetchAndActivateSuccessful(); }); } }
由 SetRemoteConfigDefaults
从初始化上下文向下游调用时,ActivateRetrievedRemoteConfigValues
会调用之前的起点 InitializeCommonDataAndStartGame
,以通过打开主菜单来启动游戏。
6. 设置 Remote Config 加载策略
如需在使用应用期间的其他时间提取并激活值,您需要再次调用这些函数,如果任何对象已缓存值,则必须通知它们执行更新。为了制定策略来重新提取 Remote Config 值,请考虑何时需要新值,以及何时开始提取和激活新值,以避免这些值在使用时发生变化。
按照目前的实现,在应用启动时提取并激活 Remote Config 值。提取操作可以在菜单更改期间隐藏,同时在转换期间阻止互动。此外,这通常是获取新值的最相关时间,因为菜单状态的变化通常可用于了解“位置”并预测会使用一个值。
看一下 Mechahamster 的菜单系统,添加界面阻止菜单刷新的最简单方法是在主菜单恢复之前(特别是在通过退出其他菜单进行访问时)调用该方法,并将界面显示方法作为 onFetchAndActivateSuccessful
回调传递。也可以对层级选择菜单执行相同的操作。
在应用启动过程中初始加载时,所有通过主菜单导航的菜单导航都将由第一个菜单导航处理,而再次进入 Level Select 菜单也会导致刷新。一开始进入关卡选择菜单并不重要,因为只能通过主菜单进入,因此已包括在内。
如需在应用中启用此功能,请在主菜单和关卡选择文件中完成相关方法,这会阻止界面显示,直到 FetchAsync
和 ActivateAsync
完成:
- 打开
Assets/Hamster/Scripts/States/MainMenu.cs
并将现有的Resume
方法替换为以下代码:public override void Resume(StateExitValue results) { CommonData.mainGame.SelectAndPlayMusic(CommonData.prefabs.menuMusic, true); CommonData.mainGame.FetchRemoteConfig(InitializeUI); }
- 保存文件。
- 打开
Assets/Hamster/Scripts/States/BaseLevelSelect.cs
,将现有Resume
方法替换为以下代码:public override void Resume(StateExitValue results) { CommonData.mainGame.FetchRemoteConfig(ShowUI); }
- 保存文件。
7. 调试/验证提取行为
此时,最好执行诊断/验证检查。您可按照以下步骤手动测试应用,以及应用如何/是否提取并激活 Remote Config 值。
该信息将作为模拟器、设备或编辑器日志的一部分输出。对于 iOS,您可以在 Xcode 中查看设备和模拟器日志。对于 Android,请运行 adb logcat
来查看日志。如果您在编辑器中按“Play”按钮在 Unity 中运行代码,则“控制台”标签页中会显示日志。
- 重新构建并运行应用(在编辑器中,使用设备或模拟器)。
- 在游戏的主菜单出现后,查看游戏的日志输出,其中应包含
Debug.Log
在FetchRemoteConfig
和ActivateRetrievedRemoteConfigValues
中生成的日志。这些按钮应该会显示“正在提取数据...”以及“远程数据已加载并准备就绪”消息。请注意这些消息开头的时间戳。 - 在游戏中,按 License。
- 按确定。
- 等待游戏的主菜单出现。
- 查看游戏的日志输出,该输出应与上一步骤中的日志输出类似,并使用了新的时间戳(与您运行游戏的系统时钟上设置的时间一致)。
- 在游戏中,按 Play。
- 按 Let's Roll。
- 使用键盘上的方向键将球导航到目标,这将打开“完成关卡”菜单。
- 按级别。
- 等待 Level Select 菜单加载。
- 再次查看游戏的日志输出。它应该与前面步骤的日志消息匹配,且时间戳较新(与运行游戏的系统时钟上设置的时间一致)。
如果其中任何部分未出现在您的应用中,则提取和激活流程的某些部分(或您的设备)可能配置有误。如果第一个日志未显示,那么您的游戏可能无法启动。查看编辑器控制台或设备/模拟器日志,看看有没有关于项目/环境的警告和错误,然后展开调查 - 问题可能非常简单,只需连接到互联网即可。
如果显示了加载菜单的初始日志,但随后的任一日志未显示,请调查/重新实现 Assets/Hamster/Scripts/States/MainMenu.cs
和 Assets/Hamster/Scripts/States/BaseLevelSelect.cs
中的 Resume
方法。
8. 对代码进行插桩
现在,您已在 SetDefaultsAsync()
中配置应用内参数值,并通过 FetchAsync()
和 ActivateAsync()
提供最新版本,接下来您将在代码中引用和使用这些值。
在 Remote Config 后端中设置值、提取并激活(或同时执行这两项操作)后,这些值便可供您的应用使用。如需使用这些值,请调用 GetValue(string key)
,并选择参数键作为实参。这将返回一个 ConfigValue
,其中包含以各种受支持的类型(string
、bool
、long
、double
)的形式访问该值的属性。在此项目和大多数游戏用例中,您必须将最后两种类型转换为更惯用的 int
和 float
。为确保这些转换不会导致问题,请确保在 Remote Config 中设置的初始值未超出您将在应用代码中使用的类型的有效范围内。
- 将
using Firebase.RemoteConfig;
添加到以下文件的顶部,以导入 Remote Config:Assets/Hamster/Scripts/States/MainMenu.cs
Assets/Hamster/Scripts/MapObjects/AccelerationTile.cs
- 替换
AccelerationTile.cs
的Start
方法:
进行此更改后,加速功能块施加的力度将更改为通过 Remote Config 接收的力度。private void Start() { var remoteConfig = FirebaseRemoteConfig.DefaultInstance; Acceleration = (float)remoteConfig.GetValue(AccelerationTileForceKey).DoubleValue; }
- 修改
MainMenu.cs
的InitializeUI
方法的正文:
在此示例中,如果private void InitializeUI() { if (menuComponent == null) { menuComponent = SpawnUI<Menus.MainMenuGUI>(StringConstants.PrefabMainMenu); } var remoteConfig = FirebaseRemoteConfig.DefaultInstance; var subtitleOverride = JsonUtility.FromJson<Menus.MainMenuGUI.SubtitleOverride>( remoteConfig.GetValue(SubtitleOverrideKey).StringValue); // Only sets values if all fields of the override are non-default. if(subtitleOverride != null && subtitleOverride.IsValidOverride()) { menuComponent.MenuSubtitleText.text = subtitleOverride.text; menuComponent.MenuSubtitleText.fontSize = subtitleOverride.fontSize; menuComponent.MenuSubtitleText.color = subtitleOverride.textColor; } ShowUI(); }
subtitleOverride
在云中的所有字段均设置为非类型默认值的值,则subtitleOverride
会更改主菜单屏幕上的副标题。
9. 远程设置参数值
现在,您的应用已全面插桩,您可以在 Remote Config 服务器上配置参数和值。在此 Codelab 中,我们将使用 Firebase 控制台对其进行设置。
- 在 Firebase 控制台中,打开您的项目。
- 从菜单中选择 Remote Config 以查看 Remote Config 信息中心。
- 针对您在应用中定义的且在下表中列出的每个参数,点击添加参数,粘贴参数名称(键),选择该表中列出的数据类型,停用使用应用内默认值,然后粘贴新的默认值:
参数名称(键)
数据类型
默认值
acceleration_tile_force
数字
100
subtitle_override
JSON
{"text":"We overwrote the subtitle","fontSize":8,"textColor":{"r":0.0,"g":255.0,"b":0.0,"a":255.0}}
- 点击保存以保存更改。
- 点击发布以发布新配置,并为游戏提供新值。
- 设置这些远程参数后,再次运行应用,并观察它们如何替换原始默认值。
10. 使用 Remote Config 条件来提供变体
您可能需要根据用户所用语言、所在位置、时段或所使用的平台来为用户提供应用体验。通过 Remote Config 条件,您可以单独或组合使用这些属性和其他属性,以向用户提供不同的值(称为变体)。
条件的一个常见用途是更改 iOS 和 Android 平台的内容。请按照以下步骤实现一个条件,根据正在使用的平台为 subtitle_override
提供不同的值。
- 在 Firebase 控制台中打开项目的“Remote Config”标签页。
- 点击“
subtitle_override.
”的“修改”按钮 - 点击左下角的新增。
- 在随即显示的下拉菜单中,将鼠标悬停在条件值上,然后点击创建新条件。
- 出现提示时,将条件命名为“is iOS”或“是 Android”。如果您同时面向这两者,只需在此处选择一个,并在此 Codelab 的其余部分使用它。
- 在适用条件...下,点击选择...下拉菜单,然后选择平台。然后,选择合适的平台。
- 点击创建条件以创建条件。系统会重新显示“修改参数”对话框,您现在可以设置一个值:
- 如果您的应用以 Android 为目标平台,请将值设为:
{"text":"Level Up Android Version","fontSize":8,"textColor":{"r":0.0,"g":255.0,"b":0.0,"a":255.0}}
- 如果您以 iOS 为目标平台,请将值设置为:
{"text":"Level Up iOS Version","fontSize":8,"textColor":{"r":0.0,"g":255.0,"b":0.0,"a":255.0}}
- 如果您的应用以 Android 为目标平台,请将值设为:
- 点击保存以保存更改。
- 点击发布以发布新配置,并为游戏提供新值。
如果您再次构建并运行游戏,您应该会看到游戏的副标题被替换为针对具体平台的变体。
11. 配置 Remote Config 以接收实时更新
Remote Config 现在可以监听并处理 Remote Config 模板的更新。应用可以订阅新的实时 Remote Config API,以监听配置更改和更新后的值。
工作原理
如需监听更新,您的应用必须实现一个用于订阅 OnConfigUpdateListener
事件的方法。订阅一个或多个配置更新监听器后,系统会自动提取新的 Remote Config 模板,并调用订阅的处理程序,这些处理程序可用于执行响应逻辑,例如激活新值并将其提供给应用的其余部分。
实现实时 Remote Config
为了说明其在游戏中的工作原理,请对您的代码进行以下更改。
创建配置更新处理程序
使用 Config Update 事件的第一步是创建一个能够监听该事件的方法。将以下方法放置在 Assets/Hamster/Scripts/MainGame.cs
中:
void ActivateValuesOnConfigUpdate( object sender, ConfigUpdateEventArgs args)
{
if (args.Error != RemoteConfigError.None) {
Debug.Log($"Error occurred while listening: {args.Error}");
return;
}
Debug.Log("Updated keys: " + string.Join(", ", args.UpdatedKeys));
// Activate all fetched values and then logs.
var remoteConfig = FirebaseRemoteConfig.DefaultInstance;
remoteConfig.ActivateAsync().ContinueWithOnMainThread(
task => {
Debug.Log($"Keys from {nameof(ActivateValuesOnConfigUpdate)} activated.");
});
}
此方法会在激活新值时将更新后的键列表和成功消息输出到日志。
订阅更新事件
为了在调用事件时激活 ActivateValuesOnConfigUpdate
,请为它订阅该事件。将 Assets/Hamster/Scripts/MainGame.cs
中的 InitializeCommonDataAndStartGame()
方法替换为以下代码:
void InitializeCommonDataAndStartGame()
{
CommonData.prefabs = FindObjectOfType<PrefabList>();
CommonData.mainCamera = FindObjectOfType<CameraController>();
CommonData.mainGame = this;
Screen.orientation = ScreenOrientation.LandscapeLeft;
musicPlayer = CommonData.mainCamera.GetComponentInChildren<AudioSource>();
CommonData.gameWorld = FindObjectOfType<GameWorld>();
// Set up volume settings.
MusicVolume = PlayerPrefs.GetInt(StringConstants.MusicVolume, MaxVolumeValue);
// Set the music to ignore the listeners volume, which is used for sound effects.
CommonData.mainCamera.GetComponentInChildren<AudioSource>().ignoreListenerVolume = true;
SoundFxVolume = PlayerPrefs.GetInt(StringConstants.SoundFxVolume, MaxVolumeValue);
// Subscribes to on config update after first initial fetch and activate
FirebaseRemoteConfig.DefaultInstance.OnConfigUpdateListener += ActivateValuesOnConfigUpdate;
stateManager.PushState(new States.MainMenu());
}
新行(以 += ActivateValuesOnConfigUpdate;
结尾)会为事件处理脚本订阅该事件。
在处理程序所属的对象被销毁时退订
为了防止 null 引用错误,具有订阅事件的方法的对象在销毁该方法时必须退订该方法。将以下方法添加到 Assets/Hamster/Scripts/MainGame.cs
:
private void OnDestroy()
{
FirebaseRemoteConfig.DefaultInstance.OnConfigUpdateListener -= ActivateValuesOnConfigUpdate;
}
测试新功能
要验证新功能,请试用您构建的应用。以下步骤要求您使用真实设备读取日志并进行调试。
更改 acceleration_tile_force
并观察
启动您的应用后,在 Firebase 控制台的 Remote Config 部分中执行以下操作:
- 按“
acceleration_tile_force
”旁边的“修改”按钮。
- 将值更改为“120”然后按保存。
- 点击发布更改按钮。
- 检查日志。
- 如果您看到以“监听时出错”开头的日志消息,请阅读其余内容,并尝试使用输出的错误消息进行调试。
- 如果您看到以“已更新的密钥”开头的日志,您的应用已收到更改的值。
- 如果您没有看到上述任一情况,请查看其余的日志,然后再次查看创建配置更新处理程序中的说明,重新测试并重新检查日志以确定是否存在任何问题。
12. 恭喜!
您已使用 Remote Config 在应用中提取值,并使用条件来提供不同的变体,从而远程控制游戏内的值!
所学内容
- 如何设置和检索 Remote Config 值
- 如何对 Unity C# 代码进行插桩,以使用检索的值
- 如何将复合值/对象存储、插桩和替换为 JSON 值
- 如何使用 Remote Config 条件来提供不同的值变体
后续步骤
阅读参数值优先级,以便更好地了解当应用实例使用包含多个值的参数(由于条件或局部性)时,应用实例会获取哪些值的逻辑。