鸿蒙应用示例:实现动态环境配置切换

源码下载:https://download.csdn.net/download/zhongcongxu01/89826872

在鸿蒙系统(HarmonyOS)的应用开发过程中,动态环境配置切换是一项重要的功能。它允许开发者根据不同的运行环境(如开发、测试、生产等)来调整应用的行为。本文将详细介绍如何利用鸿蒙系统的首选项机制来实现环境配置的动态切换,并通过一个具体的示例来展示这一过程。

【添加依赖】

harmony-utils:用于获取设备唯一ID(应用卸载重装不改变)

class-transformer:用于首选项本地数据取出json字符串转class使用(如果不使用框架,class的默认值就不生效了)

oh-package.json5

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "modelVersion": "5.0.0",
  "description": "Please describe the basic information.",
  "devDependencies": {
    "@ohos/hypium": "1.0.18",
    "@ohos/hamock": "1.0.0"
  },
  "dependencies": {
    "@pura/harmony-utils": "^1.0.3",
    "class-transformer": "^0.5.1"
  },
  "dynamicDependencies": {}
}

  【添加页面】src/main/resources/base/profile/main_pages.json

1
2
3
4
5
6
{
  "src": [
    "pages/Index",
    "pages/TestSetting"
  ]
}

  【首选项工具类】src/main/ets/common/MyPreferencesUtil.ets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
import dataPreferences from '@ohos.data.preferences';
import bundleManager from '@ohos.bundle.bundleManager';
import { common } from '@kit.AbilityKit';
import { DeviceUtil } from '@pura/harmony-utils';
import { plainToClassFromExist } from 'class-transformer'
 
export class MyPreferencesUtil {
  //日志过滤器
  private static readonly LOG: string = "====MyPreferencesUtil"
  //本地保存的文件名,和文件内的key名这里打算共用一个就行
  private static readonly FILENAME_AND_KEY: string = 'FILENAME_AND_KEY'
 
  //=========单例模式,保存数据到内存实例 end=========
  static formatTimestamp(timestamp: number): string {
    const date = new Date(timestamp);
    const year = date.getFullYear();
    const month = ('0' + (date.getMonth() + 1)).slice(-2); // 月份从 0 开始,所以加 1
    const day = ('0' + date.getDate()).slice(-2);
    const hours = ('0' + date.getHours()).slice(-2);
    const minutes = ('0' + date.getMinutes()).slice(-2);
    const seconds = ('0' + date.getSeconds()).slice(-2);
 
    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  }
 
  /**
   * 从磁盘本地读取数据到内存,赋值给instance
   * @param context 在pages中使用getContext(this),在EntryAbility中,使用this.context
   */
  public static async getData(context: Context) {
    try {
      console.info(MyPreferencesUtil.LOG, 'getData');
      let options: dataPreferences.Options = { name: MyPreferencesUtil.FILENAME_AND_KEY };
      let file = await dataPreferences.getPreferences(context, options)
      console.info(MyPreferencesUtil.LOG, 'getData', 'file succ');
      let value = file.getSync(MyPreferencesUtil.FILENAME_AND_KEY, JSON.stringify(MySetting.getInstance()))
      console.info(MyPreferencesUtil.LOG, 'getData', 'get succ', 'value:' + value);
      MySetting.updateInstance(value.toString())
      console.info(MyPreferencesUtil.LOG, 'getData', 'assign succ');
      let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT;
      let data = bundleManager.getBundleInfoForSelfSync(bundleFlags); //开始保存app的版本名称和版本号
      MySetting.getInstance().mEnvirInfo.versionName = data.versionName
      MySetting.getInstance().mEnvirInfo.versionCode = data.versionCode
      MySetting.getInstance().mEnvirInfo.deviceId = DeviceUtil.getDeviceId()
      MySetting.getInstance().mEnvirInfo.installTime = MyPreferencesUtil.formatTimestamp(data.installTime)
      MySetting.getInstance().mEnvirInfo.updateTime = MyPreferencesUtil.formatTimestamp(data.updateTime)
      console.info(MyPreferencesUtil.LOG, 'getData', 'set versionName and versionCode succ');
    } catch (e) {
      console.error(MyPreferencesUtil.LOG, 'getData', JSON.stringify(e));
    }
  }
 
  /**
   * 数据备份至磁盘本地
   *
   * @param context getContext(this)
   */
  public static async saveData(context: common.UIAbilityContext) {
    try {
      console.info(MyPreferencesUtil.LOG, 'saveData');
      let file = await dataPreferences.getPreferences(context, MyPreferencesUtil.FILENAME_AND_KEY)
      console.info(MyPreferencesUtil.LOG, 'saveData', 'file succ');
      await file.put(MyPreferencesUtil.FILENAME_AND_KEY, JSON.stringify(MySetting.getInstance()))
      console.info(MyPreferencesUtil.LOG, 'saveData', 'put succ');
      await file.flush()
      console.info(MyPreferencesUtil.LOG, 'saveData', 'flush succ');
    } catch (e) {
      console.error(MyPreferencesUtil.LOG, 'saveData', JSON.stringify(e));
    }
  }
}
 
//本地保存的配置信息模板
export class MySetting {
  //不希望被别人用new MySetting()造成误操作,所以这里用单例
  //=========单例模式,保存数据到内存实例 start=========
  private static instance: MySetting;
 
  private constructor() {
  }
 
  /**
   * 通过单例,获取本地映射到内存中的实例
   * @returns 内存中本地数据的实例
   */
  public static getInstance() {
    if (!MySetting.instance) {
      MySetting.instance = new MySetting();
    }
    return MySetting.instance;
  }
 
  //从本地信息修改的时候,需要修改当前配置信息
  public static updateInstance(value: string) {
    // MySetting.instance = JSON.parse(value)
    MySetting.instance = plainToClassFromExist(new MySetting(), JSON.parse(value))
  }
 
  /** 本地要保存的用户信息 */
  public mUserInfo: UserInfo = new UserInfo()
  // 本地要保存的环境信息
  public mEnvirInfo: EnvirInfo = new EnvirInfo()
}
 
/**
 * 用户信息
 */
export class UserInfo {
  /**
   * 用户登录成功后要保存的token
   */
  public token: string = ""
  memberId: string = ""
  unionId: string = ""
  unionCode: string = ""
}
 
/**
 * 环境信息
 */
export class EnvirInfo {
  /**
   * 首次启动的弹窗是否点击过“同意”。true:已同意,false:没同意
   */
  public isAgreement: boolean = false
  //首次启动是否查看过引导页,true:看过,false:没看过
  public isGuideScreen: boolean = false
  /** 蒙层引导页弹出次数(首页) */
 
  private launchGuideNum: number = 0;
 
  public getLaunchGuideNum() {
    return this.launchGuideNum;
  }
 
  public addLaunchGuideNum() {
    this.launchGuideNum += 1;
  }
 
  /** 蒙层引导页弹出次数(我的页面)  */
  private launchGuideMyNum: number = 0;
 
  public addLaunchGuideMyNum() {
    this.launchGuideMyNum += 1;
  }
 
  public getLaunchGuideMyNum() {
    return this.launchGuideMyNum;
  }
 
  /**
   * 当前app的版本名称
   */
  public versionName: string = ''
  /**
   * 当前app的版本号
   */
  public versionCode: number = 0
  /**appid*/
  public deviceId: string = ""
  //缓存信息
  public cacheSize: number = 0
  public installTime: string = ""
  public updateTime: string = ""
  public defaultEnv: EnvInfoKey = EnvInfoKey.GCC //默认环境
}
 
export enum EnvInfoKey {
  GCC = "GCC",
  ORD = "ORD",
  CAS = "CAS",
  PUB = "PUB",
  MBC = "MBC",
  TPL = "TPL"
}
 
export class EnvInfo {
  static env: Record<EnvInfoKey, EnvInfo> = {
    [EnvInfoKey.GCC]: new EnvInfo(EnvInfoKey.GCC, "通用环境", 'http://web.GCC.com', 'https://api.GCC.com'),
    [EnvInfoKey.ORD]: new EnvInfo(EnvInfoKey.ORD, "普通环境", "https://ORD", "https://apit.ORD"),
    [EnvInfoKey.CAS]: new EnvInfo(EnvInfoKey.CAS, "云服务环境", "https://CAS", "https://api.CAS"),
    [EnvInfoKey.PUB]: new EnvInfo(EnvInfoKey.PUB, "公共环境", "https://mPUB", "https://api.PUB"),
    [EnvInfoKey.MBC]: new EnvInfo(EnvInfoKey.MBC, "多包配置", "https://mMBC", "https://api.MBC"),
    [EnvInfoKey.TPL]: new EnvInfo(EnvInfoKey.TPL, "模板环境", "https://TP", "https://api.TPL")
  }
  typeValue: EnvInfoKey
  typeName: string = '普通环境'
  webHost: string = 'https://xxx' //网页域名(业务域名)
  apiHost: string = 'https://xxx' //接口域名
 
  constructor(typeValue: EnvInfoKey, typeName: string, webHost: string, apiHost: string) {
    this.typeValue = typeValue
    this.typeName = typeName
    this.webHost = webHost
    this.apiHost = apiHost
  }
}

  【程序入口获取本地首选项信息】src/main/ets/entryability/EntryAbility.ets

1
2
3
4
5
6
7
8
9
10
11
12
13
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { MyPreferencesUtil } from '../common/MyPreferencesUtil';
 
export default class EntryAbility extends UIAbility {
  onCreate(): void {
    MyPreferencesUtil.getData(this.context)
  }
 
  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index');
  }
}

  【配置页】src/main/ets/pages/TestSetting.ets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//测试设置页面
import { EnvInfo, MyPreferencesUtil, MySetting, UserInfo } from '../common/MyPreferencesUtil';
import { common } from '@kit.AbilityKit';
import deviceInfo from '@ohos.deviceInfo';
 
class ItemBean {
  name: string = ""
  value: string = ""
}
 
@Entry
@Component
struct TestSetting {
  private select: number = 0
  private fruits: string[] = []
  @State textValue: string = ''
  @State dataArr: Array<ItemBean> = []
  @State defaultEnvInfo: EnvInfo | undefined = undefined
 
  onCancel() {
    console.info('Callback when the first button is clicked')
  }
 
  onAccept() {
    console.info('Callback when the second button is clicked')
  }
 
  exitApp() {
    console.info('Click the callback in the blank area')
  }
 
  aboutToAppear(): void {
    this.defaultEnvInfo = EnvInfo.env[MySetting.getInstance().mEnvirInfo.defaultEnv]
    //读取本地配置信息渲染到列表
    this.fruits.length = 0
    let i = 0
    for (let key of Object.keys(EnvInfo.env)) {
      this.fruits.push(EnvInfo.env[key].typeName)
      if (MySetting.getInstance().mEnvirInfo.defaultEnv == key) {
        this.select = i
      }
      i++
    }
 
    this.dataArr = [{
      'name': 'versionName',
      'value': MySetting.getInstance().mEnvirInfo.versionName
    }, {
      'name': 'versionCode',
      'value': MySetting.getInstance().mEnvirInfo.versionCode.toString()
    }, {
      'name': 'installTime',
      'value': MySetting.getInstance().mEnvirInfo.installTime
    }, {
      'name': 'updateTime',
      'value': MySetting.getInstance().mEnvirInfo.updateTime
    }, {
      'name': 'deviceId',
      'value': MySetting.getInstance().mEnvirInfo.deviceId
    }, {
      'name': '设备市场名称',
      'value': deviceInfo.marketName
    }, {
      'name': '当前环境类型',
      'value': this.defaultEnvInfo.typeName
    }, {
      'name': 'web业务域名',
      'value': this.defaultEnvInfo.webHost
    }, {
      'name': 'api接口域名',
      'value': this.defaultEnvInfo.apiHost
    }
    ]
  }
 
  build() {
    Scroll() {
      Column() {
        Text().height('34lpx')
        Text('基本信息')
          .fontSize('48lpx')
          .fontWeight(FontWeight.Normal)
        Text().height('72lpx')
        ForEach(this.dataArr, (item: ItemBean) => {
          Stack() {
            Text()
              .width('718lpx').height('144lpx')
              .borderRadius('40lpx')
              .backgroundColor('#eeeeee')
            Column() {
              Text(item.name)
                .fontSize('30lpx')
                .fontColor('#232A35')
                .margin({ left: '50lpx' })
              Text(item.value)
                .fontSize('26lpx')
                .fontColor('#718193')
                .margin({ top: '20lpx', left: '50lpx' })
            }.width('100%')
            .alignItems(HorizontalAlign.Start)
            .onClick(() => {
              if (item.name == '当前环境类型') {
                TextPickerDialog.show({
                  range: this.fruits,
                  selected: this.select,
                  disappearTextStyle: { color: Color.Red, font: { size: 15, weight: FontWeight.Lighter } },
                  textStyle: { color: Color.Black, font: { size: 20, weight: FontWeight.Normal } },
                  selectedTextStyle: { color: Color.Blue, font: { size: 30, weight: FontWeight.Bolder } },
                  onAccept: (value: TextPickerResult) => {
                    // 设置select为按下确定按钮时候的选中项index,这样当弹窗再次弹出时显示选中的是上一次确定的选项
                    this.select = value.index as number;
 
                    console.info(`this.select:${this.select}`)
                    //清空内存中用户信息
                    MySetting.getInstance().mUserInfo = new UserInfo()
                    //修改内存中环境信息
                    this.fruits
                    for (let key of Object.keys(EnvInfo.env)) {
                      if (this.fruits[this.select] == EnvInfo.env[key].typeName) {
                        MySetting.getInstance().mEnvirInfo.defaultEnv = EnvInfo.env[key].typeValue
                        break;
                      }
                    }
                    //保存内存中的信息 将内存中的所有信息写入到磁盘首选项
                    MyPreferencesUtil.saveData(getContext() as common.UIAbilityContext)
                    //退出app
                    let context = getContext(this) as common.UIAbilityContext
                    context.terminateSelf()
                  },
                  onCancel: () => {
                    console.info("TextPickerDialog:onCancel()")
                  },
                  onChange: (value: TextPickerResult) => {
                    console.info("TextPickerDialog:onChange()" + JSON.stringify(value))
                  }
                })
              }
            })
          }.margin({ bottom: '16lpx' })
        })
      }.width('100%')
    }.scrollBar(BarState.Off)
  }
}

  【主页】src/main/ets/pages/Index.ets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { router } from '@kit.ArkUI';
import { EnvInfo, MySetting } from '../common/MyPreferencesUtil';
import { bundleManager } from '@kit.AbilityKit';
 
@Entry
@Component
struct Index {
  aboutToAppear(): void {
    let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT;
    let data = bundleManager.getBundleInfoForSelfSync(bundleFlags); //开始保存app的版本名称和版本号
    console.info(`data:${JSON.stringify(data)}`)
  }
  build() {
    Column() {
      Text(`当前环境信息:${JSON.stringify(EnvInfo.env[MySetting.getInstance().mEnvirInfo.defaultEnv])}`)
      Button('本地信息、环境配置页').onClick(() => {
        router.pushUrl({ url: 'pages/TestSetting' })
      })
    }
    .height('100%')
    .width('100%')
  }
}

  

 

posted @   zhongcx  阅读(30)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示