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

源码下载:
在鸿蒙系统(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%' ) } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了