:了解Context)
前言之前在封裝圖片滑動驗證還有當下的一個自適應背景顏色功能時都需要獲取到image.PixelMap對象于是就使用了getMediaContent方法代碼如下const resourceMgr: resourceManager.ResourceManager this.getUIContext().getHostContext()!!.resourceManager; const fileData: Uint8Array await resourceMgr.getMediaContent($r(app.media.banner_001).id) const buffer fileData.buffer as ArrayBuffer; const imageSource: image.ImageSource image.createImageSource(buffer); const pixelMap: image.PixelMap await imageSource.createPixelMap();然而以上的代碼卻報了如下錯誤Failed to get media data in getMediaFunc后續(xù)經(jīng)過排查發(fā)現(xiàn)了問題原因因為我是在單獨的共享包中調(diào)用的造成了跨模塊訪問資源時 Context 不正確所以導致了資源獲取失敗。解決以上的問題也很簡單通過createModuleContext創(chuàng)建目標模塊的Context然后再獲取資源管理器即可代碼如下const ctx this.getUIContext().getHostContext()!! const context await application.createModuleContext(ctx, 模塊名字)當然了還有一種解決方式直接使用getMediaByName方法傳入資源的名字也可以。const resourceMgr: resourceManager.ResourceManager this.getUIContext().getHostContext()!!.resourceManager; const fileData: Uint8Array await resourceMgr.getMediaByName(banner_001) const buffer fileData.buffer as ArrayBuffer; const imageSource: image.ImageSource image.createImageSource(buffer); const pixelMap: image.PixelMap await imageSource.createPixelMap();我們可以發(fā)現(xiàn)很多場景下都會用到Context比如showToast比如獲取緩存路徑等等那么什么是ContextContext在實際的開發(fā)中又是扮演著怎樣的角色呢下面我們就來一起了解一下。什么是Context官方解讀為Context是應用中對象的上下文其提供了應用的一些基礎信息例如resourceManager資源管理、applicationInfo當前應用信息、area文件分區(qū)等。我們在實際的開發(fā)中可能遇到過不同的Context比如最常見的是UIContext然后就是ApplicationContext除此之外還有很多的Context比如基類Context、ApplicationContext、AbilityStageContext、UIAbilityContext、ExtensionContext等Context不同類型Context的獲取方式與使用場景也是不同的。不同類型Context的繼承關系如下不同類型Context的獲取方式和使用場景如下Context類型說明獲取方式使用場景ApplicationContext應用的全局上下文提供應用級別的信息和能力。- 從API version 14開始可以直接使用getApplicationContext獲取。- API version 14以前版本只能使用其他Context實例的getApplicationContext方法獲取。- 獲取當前應用的基本信息。- 獲取應用級別的文件路徑。- 獲取和修改加密分區(qū)。- 監(jiān)聽應用前后臺變化。AbilityStageContext模塊級別的上下文提供模塊級別的信息和能力。- 如果需要獲取當前AbilityStage的Context可以直接通過AbilityStage實例獲取context屬性。- 如果需要獲取同一應用中其他Module的Context可以通過createModuleContext方法。- 獲取當前模塊的基本信息。- 獲取模塊級別的文件路徑。UIAbilityContextUIAbility組件對應的上下文提供UIAbility對外的信息和能力。- 通過UIAbility實例直接獲取context屬性。- 在UIAbility的窗口中加載的UI組件實例需要使用ohos.arkui.UIContext提供的getHostContext方法。- 獲取當前UIAbility的基本信息。- 啟動其他應用或元服務、連接/斷連系統(tǒng)應用創(chuàng)建的ServiceExtensionAbility等。- 銷毀自身的UIAbility。ExtensionContextExtensionAbility組件對應的上下文每種類型的ExtensionContext提供不同的信息和能力。通過ExtensionAbility實例直接獲取Context屬性。不同類型的ExtensionAbility對應的Context提供的能力不同。以輸入法上下文InputMethodExtensionContext為例主要提供如下能力- 獲取InputMethodExtensionAbility的基本信息。- 銷毀當前輸入法。UIContextArkUI的UI實例上下文提供UI操作相關的能力。與上述其他類型的Context無直接關系。- 在UI組件內(nèi)獲取UIContext直接使用組件內(nèi)置的getUIContext方法。- 在存在Window實例的情況下使用ohos.window提供的getUIContext方法。主要用于UI實例中UI相關操作例如- 獲取當前UI實例的字體。- 顯示不同類型的彈框。- 設置軟鍵盤彈出時UI避讓模式。一、ApplicationContextApplicationContext 是應用級別的全局上下文繼承自 Context提供了應用生命周期監(jiān)聽、進程管理、應用環(huán)境設置等應用級別的管控能力。有一個需要注意的點是一個應用只有一個 ApplicationContext 實例可以在 UIAbility、ExtensionAbility、AbilityStage 中獲取。獲取方式從 API version 14 開始可以直接通過application.getApplicationContext()獲取無需依賴 Context 基類API version 14 以前只能使用其他 Context 實例的getApplicationContext()方法獲取。// 方式一API 14 直接獲取推薦 import { application, common } from kit.AbilityKit; let appContext: common.ApplicationContext application.getApplicationContext(); // 方式二通過其他 Context 實例獲取API 9 通用 // 在 UIAbility 中 import { UIAbility, common } from kit.AbilityKit; export default class EntryAbility extends UIAbility { onCreate(want: Want): void { let appContext: common.ApplicationContext this.context.getApplicationContext(); } }核心方法與能力方法/能力說明on(abilityLifecycle)注冊應用內(nèi)組件生命周期變化監(jiān)聽on(environment)注冊系統(tǒng)環(huán)境變化監(jiān)聽語言、顏色模式等on(systemMemoryLevel)注冊系統(tǒng)內(nèi)存變化監(jiān)聽off(...)取消對應的事件監(jiān)聽getApplicationInfo()獲取應用信息繼承自 ContextgetCacheDir()/ getFilesDir()獲取應用級文件路徑繼承自 Contextarea獲取/設置加密分區(qū)模式setColorMode()設置應用顏色模式setLanguage()設置應用語言典型使用場景獲取當前應用的基本信息name、應用版本等獲取應用級別的文件路徑如緩存目錄、文件目錄獲取和修改加密分區(qū)監(jiān)聽應用前后臺變化、系統(tǒng)內(nèi)存變化、環(huán)境變化設置應用語言和顏色模式import { application, common, AbilityConstant } from kit.AbilityKit; import { hilog } from kit.PerformanceAnalysisKit; const DOMAIN: number 0x0000; const TAG: string AppContextDemo; // 獲取應用上下文并監(jiān)聽生命周期 let appContext: common.ApplicationContext application.getApplicationContext(); let abilityLifecycleCallback: AbilityLifecycleCallback { onAbilityCreate(ability): void { hilog.info(DOMAIN, TAG, Ability創(chuàng)建: ${ability.context.abilityInfo.name}); }, onAbilityForeground(ability): void { hilog.info(DOMAIN, TAG, Ability切到前臺: ${ability.context.abilityInfo.name}); }, onAbilityBackground(ability): void { hilog.info(DOMAIN, TAG, Ability切到后臺: ${ability.context.abilityInfo.name}); }, onAbilityDestroy(ability): void { hilog.info(DOMAIN, TAG, Ability銷毀: ${ability.context.abilityInfo.name}); }, onWindowStageCreate(ability, windowStage) { console.info(AbilityLifecycleCallback onWindowStageCreate ability: ${ability}); console.info(AbilityLifecycleCallback onWindowStageCreate windowStage: ${windowStage}); }, onWindowStageActive(ability, windowStage) { console.info(AbilityLifecycleCallback onWindowStageActive ability: ${ability}); console.info(AbilityLifecycleCallback onWindowStageActive windowStage: ${windowStage}); }, onWindowStageInactive(ability, windowStage) { console.info(AbilityLifecycleCallback onWindowStageInactive ability: ${ability}); console.info(AbilityLifecycleCallback onWindowStageInactive windowStage: ${windowStage}); }, onWindowStageDestroy(ability, windowStage) { console.info(AbilityLifecycleCallback onWindowStageDestroy ability: ${ability}); console.info(AbilityLifecycleCallback onWindowStageDestroy windowStage: ${windowStage}); }, onAbilityContinue(ability) { console.info(AbilityLifecycleCallback onAbilityContinue ability: ${ability}); } } appContext.on(abilityLifecycle,abilityLifecycleCallback) // 獲取應用級緩存目錄 let cacheDir: string appContext.cacheDir; hilog.info(DOMAIN, TAG, 緩存目錄: ${cacheDir});二、AbilityStageContext模塊上下文AbilityStageContext 是 AbilityStage 的上下文環(huán)境繼承自 Context提供訪問特定于 AbilityStage 的資源的能力包括獲取 AbilityStage 對應的 ModuleInfo 對象、環(huán)境變化對象。AbilityStage 是 Module 級別的組件管理器與 Module 一一對應應用的 HAP/HSP 在首次加載時會創(chuàng)建一個 AbilityStage 實例。獲取方式通過 AbilityStage 實例的context屬性直接獲取如果需要獲取同一應用中其他 Module 的 Context可以通過createModuleContext方法創(chuàng)建。import { AbilityStage } from kit.AbilityKit; export default class MyAbilityStage extends AbilityStage { onCreate(): void { // 獲取當前模塊的 AbilityStageContext let stageContext: common.AbilityStageContext this.context; // 獲取當前模塊信息 let moduleInfo: common.ModuleInfo stageContext.currentHapModuleInfo; console.info(模塊名: ${moduleInfo.moduleName}); } }核心屬性與方法屬性/方法說明currentHapModuleInfo獲取當前 AbilityStage 對應的 ModuleInfo 對象getCacheDir()/ getFilesDir()獲取模塊級文件路徑繼承自 Context典型使用場景獲取當前模塊的基本信息moduleName、moduleInfo 等獲取模塊級別的文件路徑在應用啟動框架AppStartup中作為啟動任務的入?yún)鬟f三、UIAbilityContextUIAbility 組件上下文UIAbilityContext 是 UIAbility 組件的上下文繼承自 Context。每個 UIAbility 組件實例化時系統(tǒng)都會自動創(chuàng)建對應的 UIAbilityContext。開發(fā)者可以通過它獲取組件信息 AbilityInfo、應用信息 ApplicationInfo、拉起其他 UIAbility、連接系統(tǒng)服務、銷毀 UIAbility 等。獲取方式在 UIAbility 中直接通過this.context獲取在頁面/組件中需要先獲取 UIContext再通過getHostContext()獲取關聯(lián)的 UIAbilityContext。// 方式一在 UIAbility 中直接獲取 import { UIAbility, Want } from kit.AbilityKit; export default class EntryAbility extends UIAbility { onCreate(want: Want): void { let uiAbilityContext: common.UIAbilityContext this.context; console.info(Ability名稱: ${uiAbilityContext.abilityInfo.name}); } } // 方式二在頁面組件中獲取推薦方式 Entry Component struct Index { build() { Column() { Button(獲取上下文) .onClick(() { // 通過 getUIContext 獲取 UIContext再通過 getHostContext 獲取 UIAbilityContext let uiAbilityContext: common.UIAbilityContext this.getUIContext().getHostContext() as common.UIAbilityContext; console.info(Ability名稱: ${uiAbilityContext.abilityInfo.name}); }) } } }核心方法與能力方法說明startAbility(want)啟動其他 UIAbility支持 callback 和 Promise 兩種方式startAbilityForResult(want)啟動 UIAbility 并獲取返回結果terminateSelf()銷毀當前 UIAbilityterminateSelfWithResult()銷毀當前 UIAbility 并返回結果connectServiceExtensionAbility(want)連接 ServiceExtensionAbilitydisconnectServiceExtensionAbility(connection)斷開 ServiceExtensionAbility 連接abilityInfo獲取當前 UIAbility 的配置信息currentHapModuleInfo獲取當前 HAP 模塊信息config獲取配置信息典型使用場景獲取當前 UIAbility 的基本信息啟動其他應用或元服務連接/斷連系統(tǒng)應用創(chuàng)建的 ServiceExtensionAbility銷毀自身的 UIAbility頁面間傳參與結果回傳import { UIAbility, Want, common } from kit.AbilityKit; import { hilog } from kit.PerformanceAnalysisKit; import { BusinessError } from kit.BasicServicesKit; const DOMAIN: number 0x0000; const TAG: string UIAbilityContextDemo; export default class EntryAbility extends UIAbility { onCreate(want: Want): void { let context: common.UIAbilityContext this.context; // 啟動另一個 UIAbility let wantInfo: Want { bundleName: com.example.demo, abilityName: OtherAbility }; context.startAbility(wantInfo) .then(() { hilog.info(DOMAIN, TAG, 啟動成功); }) .catch((err: BusinessError) { hilog.error(DOMAIN, TAG, 啟動失敗: ${err.message}); }); } onBackPress(): boolean { // 銷毀當前 UIAbility this.context.terminateSelf(); return true; } }四、ExtensionContext擴展能力上下文ExtensionContext 是 ExtensionAbility 組件對應的上下文繼承自 Context。每種類型的 ExtensionAbility 都有對應的 ExtensionContext 子類提供不同的信息和能力。ExtensionContext 本身是基類實際使用時通常操作其子類。常見子類子類對應 ExtensionAbility核心能力FormExtensionContextFormExtensionAbility查詢該擴展的FormExtensionAbility所屬HAP包等信息。InputMethodExtensionContextInputMethodExtensionAbility獲取輸入法信息、銷毀當前輸入法AccessibilityExtensionContextAccessibilityExtensionAbility配置輔助功能、查詢節(jié)點信息、手勢注入PushExtensionContextPushExtensionAbility訪問推送擴展資源AgentExtensionContextAgentExtensionAbility訪問智能體配置的 AgentCard 信息AppServiceExtensionContextAppServiceExtensionAbility連接/斷開系統(tǒng)應用后臺服務獲取方式通過 ExtensionAbility 實例直接獲取context屬性。import { FormExtensionAbility, formBindingData } from kit.FormKit; import { Want } from kit.AbilityKit; export default class MyFormExtensionAbility extends FormExtensionAbility { onAddForm(want: Want) { console.info(FormExtensionAbility onAddForm, want: ${want.abilityName}); let extensionContext this.context; let hapInfo extensionContext.currentHapModuleInfo; console.info(HAP name is: ${hapInfo.name}); let dataObj1: Recordstring, string { temperature: 11c, time: 11:00 }; let obj1: formBindingData.FormBindingData formBindingData.createFormBindingData(dataObj1); return obj1; } };典型使用場景不同類型的 ExtensionAbility 對應不同的后臺服務場景獲取當前 ExtensionAbility 的基本信息啟動/停止/綁定/解綁其他 Ability銷毀當前 ExtensionAbility五、UIContextUI 實例上下文UIContext 是 UI 實例運行環(huán)境的抽象概念用于關聯(lián)窗口與 UI 頁面管理組件、布局、動畫以及交互事件等 UI 功能。UIContext 與上述四種 Context 無繼承關系它由窗口對象創(chuàng)建并管理每個窗口對應一個 UIContext 實例。從 API version 10 開始許多全局接口如ohos.promptAction、ohos.router、AlertDialog等因多 UI 實例場景下上下文不明確而被廢棄需要通過 UIContext 提供的對應方法獲取實例后調(diào)用。獲取方式UIContext 有三種獲取方式// 方式一在自定義組件中通過內(nèi)置方法獲取最常用 Entry Component struct Index { build() { Column() { Button(獲取UIContext) .onClick(() { let uiContext: UIContext this.getUIContext(); }) } } } // 方式二在 UIAbility 中通過窗口獲取 import { UIAbility, window } from kit.AbilityKit; export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage): void { let uiContext: UIContext windowStage.getMainWindowSync().getUIContext(); } } // 方式三通過 UIContext 靜態(tài)方法獲取 import { UIContext } from kit.ArkUI; let uiContext: UIContext | undefined UIContext.getCallingScopeUIContext();核心方法與能力UIContext 提供了大量 UI 相關的接口用于替代被廢棄的全局接口方法說明替代的全局接口getPromptAction()獲取 PromptAction 對象用于彈窗、Toast 等ohos.promptActiongetRouter()獲取 Router 對象用于頁面路由ohos.routershowAlertDialog()顯示警告彈窗AlertDialog.show()showActionSheet()顯示列表選擇彈窗ActionSheet.show()getComponentUtils()獲取 ComponentUtils 對象用于組件坐標和尺寸信息ohos.arkui.componentUtilsgetMediaQuery()獲取 MediaQuery 對象用于媒體查詢ohos.mediaquerygetHostContext()獲取關聯(lián)的 UIAbilityContext 或 ExtensionContextgetContext()getSharedLocalStorage()獲取共享的 LocalStorage-runScopedTask()在指定 UI 上下文中執(zhí)行任務-典型使用場景替代全局接口進行彈窗操作AlertDialog、ActionSheet、PromptAction頁面路由跳轉獲取組件坐標和尺寸信息在頁面/組件中獲取關聯(lián)的 UIAbilityContext多 UI 實例場景下確保 UI 操作在正確的上下文中執(zhí)行import { UIContext, PromptAction, router } from kit.ArkUI; import { common } from kit.AbilityKit; import { BusinessError } from kit.BasicServicesKit; Entry Component struct Index { build() { Column() { // 彈窗示例 Button(顯示Toast) .onClick(() { let uiContext: UIContext this.getUIContext(); let promptAction: PromptAction uiContext.getPromptAction(); promptAction.showToast({ message: 這是一條Toast消息, duration: 2000 }); }) // 頁面路由示例 Button(跳轉頁面) .onClick(() { let uiContext: UIContext this.getUIContext(); let routerInstance: router.Router uiContext.getRouter(); routerInstance.pushUrl({ url: pages/SecondPage }); }) // 警告彈窗示例 Button(顯示警告彈窗) .onClick(() { let uiContext: UIContext this.getUIContext(); uiContext.showAlertDialog({ title: 提示, message: 確定要執(zhí)行此操作嗎, onConfirm: () { console.info(用戶點擊了確認); } }); }) // 獲取 UIAbilityContext Button(獲取AbilityContext) .onClick(() { let uiContext: UIContext this.getUIContext(); let abilityContext: common.UIAbilityContext uiContext.getHostContext() as common.UIAbilityContext; console.info(包名: ${abilityContext.abilityInfo.bundleName}); }) } .width(100%) .height(100%) } }AppStorage獲取Context在HarmonyOS應用開發(fā)中工具類非UI組件、非UIAbility的普通class無法直接獲取Context對象。標準做法是在UIAbility的生命周期回調(diào)中獲取Context后通過AppStorage.setOrCreate將其保存為全局狀態(tài)數(shù)據(jù)隨后在工具類中通過AppStorage.get取出使用。AppStorage是應用啟動時創(chuàng)建的單例提供應用級別的中心存儲支持主線程內(nèi)多個UIAbility實例間的數(shù)據(jù)共享。示例代碼以下示例分為兩個步驟第一步在EntryAbility中存儲Context第二步在工具類中獲取Context。步驟一在EntryAbility的onCreate回調(diào)中存儲Contextimport { UIAbility, AbilityConstant, Want } from kit.AbilityKit; export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // 將當前UIAbility的上下文存入AppStoragekey為appContext AppStorage.setOrCreate(appContext, this.context); } onWindowStageCreate(windowStage: window.WindowStage): void { windowStage.loadContent(pages/Index); } }步驟二在工具類中通過AppStorage獲取Contextimport { common } from kit.AbilityKit; class PreferenceUtil { // 從AppStorage中獲取UIAbilityContext static getContext(): common.UIAbilityContext { const context AppStorage.getcommon.UIAbilityContext(appContext); if (!context) { throw new Error(Context未初始化請確認AppStorage中已存儲appContext); } return context; } // 示例使用Context獲取文件路徑 static getFilesDir(): string { const context this.getContext(); return context.filesDir; } }補充說明存儲時機Context應在onCreate回調(diào)中存儲此時this.context已經(jīng)可用。避免在onWindowStageCreate之前的其他階段獲取以免Context尚未初始化。類型安全AppStorage.get返回值為泛型類型使用時應明確指定泛型參數(shù)如common.UIAbilityContext并通過空值檢查確保Context已正確存儲避免運行時異常。Context類型選擇根據(jù)實際需求選擇存儲的Context類型。UIAbilityContext提供組件級能力如獲取filesDir、resourceManager等若僅需應用級能力如監(jiān)聽應用前后臺變化可存儲ApplicationContext通過this.context.getApplicationContext()獲取。非UI場景的通用方案此方法不僅適用于工具類也適用于其他非ArkUI頁面如Worker線程外的純邏輯模塊中需要訪問Context的場景。Worker線程中不支持直接使用此方式需通過消息傳遞將Context發(fā)送至Worker線程。相關總結在UI頁面中我們可以很輕松的獲取到Context但是在工具類中如何獲取呢首先可以明確的是工具類中是無法直接獲取Context的我們可以在獲取應用上下文Context后保存至AppStorage然后通過AppStorage來獲取當然了也可以通過別的方式比如全局靜態(tài)變量比如一個單例工具類等等都是可以的。