Core Technologies of HarmonyOS ## Sports Development
When developing HarmonyOS sports projects, managing different runtime environments (such as development, testing, and production environments) is a common requirement. By properly switching runtime environments, developers can conveniently debug, test, and deploy their applications. This article will introduce how to implement a project runtime environment switcher to help you efficiently manage configurations for different environments in HarmonyOS development.
Preface
In modern software development, environment management is one of the key factors in ensuring the stability and maintainability of applications. Whether it is the development, testing, or production environment, each environment may have different configuration requirements, such as API addresses, log levels, and feature toggles. By implementing a runtime environment switcher, we can easily switch between different environments without modifying the code, thereby improving development efficiency and flexibility.
I. Design of the Environment Switcher
(I) Environment Configuration Types
To support configurations for different environments, we have defined the EnvironmentConfigs
and CurrentEnvironment
types.
export type EnvironmentConfigs = Map<string, Map<string, string>>;
export interface CurrentEnvironment {
name: string;
configs: Map<string, string>;
}
Core Points Analysis:
-
EnvironmentConfigs: A mapping table where the key is the environment name (e.g.,
production
,development
), and the value is the configuration mapping table for that environment. - CurrentEnvironment: Represents the name and configurations of the current environment.
(II) Environment Type Enum
We have defined the supported environment types through an enumeration.
export enum EnvironmentType {
TYPE_PRODUCTION = "production",
TYPE_DEVELOP = "develop"
}
Core Points Analysis:
-
Enum Type: Two environment types are defined through the enumeration: production environment (
production
) and development environment (develop
). More environment types can be added as needed.
(III) Environment Management Class
The Environment
class, which is the core of the entire environment switcher, is responsible for storing environment configurations, loading the saved environment, switching environments, and notifying callbacks.
export class Environment {
private static instance: Environment;
private static readonly ENVIRONMENT_STORAGE_KEY = 'current_environment';
private currentEnvironment?: CurrentEnvironment;
private environments: EnvironmentConfigs = new Map();
private preferences: LibPreferencesSync;
private environmentChangeCallbacks: Array<(newEnvironment: CurrentEnvironment) => void> = [];
private constructor() {
this.preferences = new LibPreferencesSync();
}
public static getInstance(): Environment {
if (!Environment.instance) {
Environment.instance = new Environment();
}
return Environment.instance;
}
public initEnvironments(evn: EnvironmentConfigs) {
this.environments = evn;
this.loadSavedEnvironment();
}
private loadSavedEnvironment() {
if (!IS_PRODUCTION) {
const savedEnvironmentName = this.preferences.getValue(Environment.ENVIRONMENT_STORAGE_KEY) as string;
if (savedEnvironmentName && this.environments.has(savedEnvironmentName)) {
this.currentEnvironment = {
name: savedEnvironmentName,
configs: this.environments.get(savedEnvironmentName)!
};
} else {
this.currentEnvironment = {
name: EnvironmentType.TYPE_DEVELOP,
configs: this.environments.get(EnvironmentType.TYPE_DEVELOP)!
};
}
} else {
this.currentEnvironment = {
name: EnvironmentType.TYPE_PRODUCTION,
configs: this.environments.get(EnvironmentType.TYPE_PRODUCTION)!
};
}
}
public switchEnvironment(name: string) {
const configs = this.environments.get(name);
if (configs) {
this.currentEnvironment = { name, configs };
this.preferences.saveKeyValue(Environment.ENVIRONMENT_STORAGE_KEY, name);
this.environmentChangeCallbacks.forEach(callback => callback(this.currentEnvironment!));
}
}
public getCurrentEnvironment(): CurrentEnvironment {
return this.currentEnvironment!;
}
public getAllEnvironmentNames(): string[] {
return Array.from(this.environments.keys());
}
public registerEnvironmentChangeCallback(callback: (newEnvironment: CurrentEnvironment) => void) {
this.environmentChangeCallbacks.push(callback);
}
public unregisterEnvironmentChangeCallback(callback: (newEnvironment: CurrentEnvironment) => void) {
this.environmentChangeCallbacks = this.environmentChangeCallbacks.filter(cb => cb !== callback);
}
}
Core Points Analysis:
-
Singleton Pattern: The
getInstance
method ensures the global uniqueness of theEnvironment
instance. -
Environment Initialization: The
initEnvironments
method initializes the environment configurations. -
Loading Saved Environment: In the
loadSavedEnvironment
method, the corresponding environment configuration is loaded based on the stored environment name. -
Environment Switching: The
switchEnvironment
method switches the environment and notifies all registered callback functions. - Callback Mechanism: Supports registering and unregistering environment change callbacks to facilitate operations when the environment is switched.
II. Using the Environment Switcher
(I) Environment Switch Dialog
To facilitate environment switching for users, we have implemented an environment switch dialog called EnvironmentDialog
.
@CustomDialog
export struct EnvironmentDialog {
public controller: CustomDialogController;
private themeManager: ThemeManager = ThemeManager.getInstance();
private environment: Environment = Environment.getInstance();
public onEnvironmentChanged?: () => void;
build() {
Column() {
Text('Select Environment')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(this.themeManager.getTextPrimaryColor())
.margin({ top: 24, bottom: 16 })
List() {
ForEach(this.environment.getAllEnvironmentNames(), (envname: string) => {
ListItem() {
Row() {
Column() {
Text(envname)
.fontSize(16)
.fontColor(this.themeManager.getTextPrimaryColor())
.margin({ bottom: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
if (this.environment.getCurrentEnvironment().name === envname) {
Image($r('app.media.base_icon_select'))
.width(24)
.height(24)
.margin({ left: 8 })
}
}
.width('100%')
.padding(16)
.backgroundColor(this.themeManager.getSurfaceColor())
.borderRadius(8)
.onClick(() => {
this.environment.switchEnvironment(envname);
this.onEnvironmentChanged?.();
this.controller.close();
})
}
.margin({ bottom: 8 })
}, (envname: string) => envname)
}
.width('100%')
.layoutWeight(1)
Button('Close')
.width('100%')
.height(48)
.backgroundColor(this.themeManager.getPrimaryColor())
.margin({ top: 16 })
.onClick(() => {
this.controller.close();
})
}
.width('90%')
.padding(16)
.backgroundColor(this.themeManager.getBackgroundColor())
.borderRadius(16)
}
}
Core Points Analysis:
-
Environment List: The
ForEach
loop iterates over all environment names and generates a list item for each environment. - Current Environment Indicator: If the current environment matches the list item environment, a selection icon is displayed.
-
Environment Switching: When a list item is clicked, the
switchEnvironment
method is called to switch the environment, and the dialog is closed. -
Callback Notification: After the environment switch, the
onEnvironmentChanged
callback function is called to notify the outside that the environment has been switched.
(II) Callback Mechanism for Environment Switching
To perform related operations when the environment is switched, we can implement this by registering callback functions.
const environment = Environment.getInstance();
environment.registerEnvironmentChangeCallback((newEnvironment) => {
console.log(`Environment switched to: ${newEnvironment.name}`);
// Perform related operations after the environment switch here, such as reloading configurations, refreshing the interface, etc.
});
Core Points Analysis:
-
Register Callback: The
registerEnvironmentChangeCallback
method is used to register a callback function. - Callback Execution: The callback function is automatically called when the environment is switched.
III. Summary
By implementing a project runtime environment switcher, we can easily manage configurations for different environments in HarmonyOS sports projects. The environment switcher not only supports dynamic environment switching but also provides a callback mechanism to facilitate related operations when the environment is switched. In this way, developers can quickly switch between development, testing, and production environments without modifying the code, thereby improving development efficiency and flexibility.
In actual development, you can further extend and optimize the environment switcher based on the specific needs of your project. For example:
- Support More Environment Types: Add more environment types according to project requirements, such as testing environments, pre-release environments, etc.
- Dynamic Configuration Loading: Dynamically load environment configurations from a remote server.
- Integration with Build Tools: Integrate the environment switcher with build tools to support specifying the runtime environment during the build process.
I hope this article provides valuable insights for your HarmonyOS development journey! If you have any questions or suggestions, feel free to reach out and discuss.
Top comments (0)