diff --git "a/Stage\346\250\241\345\236\213\345\274\200\345\217\221\346\214\207\345\215\227-Context.md" "b/Stage\346\250\241\345\236\213\345\274\200\345\217\221\346\214\207\345\215\227-Context.md" index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d6b1b6bb2fb5e3c20b6d87d51a7f59a83e86b9b5 100644 --- "a/Stage\346\250\241\345\236\213\345\274\200\345\217\221\346\214\207\345\215\227-Context.md" +++ "b/Stage\346\250\241\345\236\213\345\274\200\345\217\221\346\214\207\345\215\227-Context.md" @@ -0,0 +1,574 @@ +# Stage模型的context + +## Context的简介 + +### Context的定义 + +在应用的开发过程中,我们通常会遇到需要访问特定上下文的资源的情况,如在一个ability中获取当前ability的信息。也就是,用户代码在与系统交互中,系统需要根据当前所在的上下文,给出对应的信息和提供对应的功能。 + +OpenHarmony中,Context模块提供了ability或application的上下文的能力。 + +### Context的用途 + +开发者可以通过Context访问特定ability或application的资源,或者发起对应的请求等。 + +### Context的类型 + + OpenHarmony的应用框架分为FA模型和Stage两种模型。对应存在两套Context机制适配两种应用框架模型,其中application/BaseContext属于通用的Context基类,里面包含一个属性stageMode,用来区分开发模型是FA还是Stage。 + +- FA模型 + 只有app/Context中的方法属于FA模型对应的Context。该模式下,应用级别的Context和Ability级别的Context都是该类型的实例,如果在应用级别的Context里面调用了Ability级别的方法,会产生错误。所以开发者需要注意Context实例所代表的实际含义。详见[FA模型的Context](FA模型开发指南-Context.md)。 + +- Stage模型 + 除了app/Context之外的Context都属于Stage模型,分别有application/Context、application/ApplicationContext、application/AbilityStageContext、application/ExtensionContext、application/AbilityContext、application/FormExtensionContext等Context。这些Context的介绍及使用方式将会在本文中进行说明。 + + ![contextIntroduction](figures/contextIntroduction.png) + + + +## 约束与限制 + +下文所述的部分接口为系统接口,三方应用不支持调用。接口需要的权限以及是否是系统接口可以到各个章节的所附的接口详细页面查看。 + + + + +## 相关实例 + +### application/Context + +application/Context是基类Context,里面提供了应用的一些基础信息:resourceManager、applicationInfo、cacheDir、area等,还有应用的一些基本方法:createBundleContext等。具体接口声明详见:[Context接口声明](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/Context.d.ts),接口文档详见:[Context接口文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-Context.md)。 + +在不同的Context上,同一方法或属性会存在差异,比如通过ApplicationContext获取路径和AbilityContext获取的会有所不同,具体详见[应用开发路径的使用](##应用开发路径的使用) + + + +### application/ApplicationContext + +application/ApplicationContext是应用级别的Context。和基类Context相比,应用级别的Context中提供了监听进程内组件的生命周期的能力,包括registerAbilityLifecycleCallback和unregisterAbilityLifecycleCallback两种方法。具体接口声明详见:[ApplicationContext接口声明](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/ApplicationContext.d.ts),接口文档详见:[ApplicationContext接口文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-application-applicationContext.md)。 + +#### 获取方法 + +在Ability中通过context.getApplicationContext()方法获取。 + +#### 示例代码 + +```js +import AbilityStage from "@ohos.application.AbilityStage"; + +var lifecycleid; + +export default class MyAbilityStage extends AbilityStage { + onCreate() { + console.log("MyAbilityStage onCreate") + let AbilityLifecycleCallback = { + onAbilityCreate(ability){ + console.log("AbilityLifecycleCallback onAbilityCreate ability:" + JSON.stringify(ability)); + }, + onWindowStageCreate(ability, windowStage){ + console.log("AbilityLifecycleCallback onWindowStageCreate ability:" + JSON.stringify(ability)); + console.log("AbilityLifecycleCallback onWindowStageCreate windowStage:" + JSON.stringify(windowStage)); + }, + onWindowStageActive(ability, windowStage){ + console.log("AbilityLifecycleCallback onWindowStageActive ability:" + JSON.stringify(ability)); + console.log("AbilityLifecycleCallback onWindowStageActive windowStage:" + JSON.stringify(windowStage)); + }, + onWindowStageInactive(ability, windowStage){ + console.log("AbilityLifecycleCallback onWindowStageInactive ability:" + JSON.stringify(ability)); + console.log("AbilityLifecycleCallback onWindowStageInactive windowStage:" + JSON.stringify(windowStage)); + }, + onWindowStageDestroy(ability, windowStage){ + console.log("AbilityLifecycleCallback onWindowStageDestroy ability:" + JSON.stringify(ability)); + console.log("AbilityLifecycleCallback onWindowStageDestroy windowStage:" + JSON.stringify(windowStage)); + }, + onAbilityDestroy(ability){ + console.log("AbilityLifecycleCallback onAbilityDestroy ability:" + JSON.stringify(ability)); + }, + onAbilityForeground(ability){ + console.log("AbilityLifecycleCallback onAbilityForeground ability:" + JSON.stringify(ability)); + }, + onAbilityBackground(ability){ + console.log("AbilityLifecycleCallback onAbilityBackground ability:" + JSON.stringify(ability)); + }, + onAbilityContinue(ability){ + console.log("AbilityLifecycleCallback onAbilityContinue ability:" + JSON.stringify(ability)); + } + } + // 1.通过context属性获取applicationContext + let applicationContext = this.context.getApplicationContext(); + // 2.通过applicationContext注册监听应用内生命周期 + lifecycleid = applicationContext.registerAbilityLifecycleCallback(AbilityLifecycleCallback); + console.log("registerAbilityLifecycleCallback number: " + JSON.stringify(lifecycleid)); + } + onDestroy() { + let applicationContext = this.context.getApplicationContext(); + applicationContext.unregisterAbilityLifecycleCallback(lifecycleid, (error, data) => { + console.log("unregisterAbilityLifecycleCallback success, err: " + JSON.stringify(error)); + }); + } +} +``` + + + +### application/AbilityStageContext + +application/AbilityStageContext是模块级别的Context。和基类Context相比,模块级别的Context中多了HapModuleInfo和Configuration两个信息。具体接口声明详见:[AbilityStageContext接口声明](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/AbilityStageContext.d.ts),接口文档详见:[AbilityStageContext接口文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-abilitystagecontext.md)。 + +#### 获取方法 + +可以直接在AbilityStage中通过context属性获取。 + +#### 示例代码 + +```js +export default class MyAbilityStage extends AbilityStage { + onCreate() { + // 属性context就是AbilityStageContext类型的 + console.log('HapModuleInfo is ' + this.context.currentHapModuleInfo); + } +} +``` + + + +### application/AbilityContext + +Stage模型下,每个Ability中都包含了一个Context属性。Ability功能主要是处理生命周期,其余操作Ability的方法(如startAbility、connectAbility等)都是在AbilityContext中实现的。具体接口声明详见:[AbilityContext接口声明](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/AbilityContext.d.ts),接口文档详见:[AbilityContext接口文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-ability-context.md)。 + +#### 获取方法 + +在Ability中通过context属性获取。 + +#### 示例代码 + +```js +import Ability from '@ohos.application.Ability' + +export default class MainAbility extends Ability { + onCreate(want, launchParam) { + console.log("[Demo] MainAbility onCreate") + globalThis.abilityWant = want; + } + + onDestroy() { + console.log("[Demo] MainAbility onDestroy") + } + + onWindowStageCreate(windowStage) { + // Main window is created, set main page for this ability + console.log("[Demo] MainAbility onWindowStageCreate") + + // 在这里获取AbilityContext,打印ability的信息 + let context = this.context; + console.log("[Demo] MainAbility bundleName " + context.abilityInfo.bundleName) + + windowStage.setUIContent(this.context, "pages/index", null) + } + + onWindowStageDestroy() { + // Main window is destroyed, release UI related resources + console.log("[Demo] MainAbility onWindowStageDestroy") + } + + onForeground() { + // Ability has brought to foreground + console.log("[Demo] MainAbility onForeground") + } + + onBackground() { + // Ability has back to background + console.log("[Demo] MainAbility onBackground") + } +}; +``` + + + +### application/ExtensionContext + +application/ExtensionContext是ExtensionAbility的基类Context,里面提供了应用的一些基础信息:hapModuleInfo、extensionAbilityInfo等信息。具体接口声明详见:[ExtensionContext接口声明](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/ExtensionContext.d.ts),接口文档详见:[ExtensionContext接口文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-extension-context.md)。 + + + +### application/ServiceExtensionContext + +application/ServiceExtensionContext是ServiceExtension的Context。相比ExtensionContext提供了startAbility的能力。具体接口声明详见:[ServiceExtensionContext接口声明](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/ServiceExtensionContext.d.ts),接口文档详见:[ServiceExtensionContext接口文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-service-extension-context.md)。 + +#### 获取方法 + +在ServiceExtension中通过context属性获取。 + +#### 示例代码 + +```js +import Extension from '@ohos.application.ServiceExtensionAbility' +import rpc from '@ohos.rpc' + +class StubTest extends rpc.RemoteObject { + constructor(des) { + super(des); + } + onRemoteRequest(code, data, reply, option) { + return true; + } + queryLocalInterface(descriptor) { + return null; + } + getInterfaceDescriptor() { + return ""; + } + sendRequest(code, data, reply, options) { + return null; + } + getCallingPid() { + return REQUEST_VALUE; + } + getCallingUid() { + return REQUEST_VALUE; + } + attachLocalInterface(localInterface, descriptor){} +} + +export default class ServiceExtAbility extends Extension { + onCreate(want) { + console.log("[Demo] ServiceExtAbility onDestroy"); + // 在这里获取ServiceExtensionContext,打印extensionAbilityInfo的信息 + let context = this.context; + console.log("[Demo] ServiceExtAbility extensionAbilityInfo: " + JSON.stringify(context.extensionAbilityInfo)); + } + + onRequest(want, startId) { + console.log("[Demo] ServiceExtAbility onDestroy"); + } + + onConnect(want) { + console.log("[Demo] ServiceExtAbility onDestroy"); + return new StubTest("test"); + } + + onDisconnect(want) { + console.log("[Demo] ServiceExtAbility onDestroy"); + } + + onDestroy() { + console.log("[Demo] ServiceExtAbility onDestroy"); + } +} +``` + + + +### application/FormExtensionContext + +application/FormExtensionContext是FormExtension的Context。相比ExtensionContext提供了startAbility的能力。具体接口声明详见:[FormExtensionContext接口声明](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/FormExtensionContext.d.ts),接口文档详见:[FormExtensionContext接口文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-formextensioncontext.md)。 + +#### 获取方法 + +在FormExtension中通过context属性获取。 + +#### 示例代码 + +```js +import FormExtension from '@ohos.application.FormExtension'; +import formBindingData from '@ohos.application.formBindingData'; + +export default class FormAbility extends FormExtension { + onCreate(want) { + console.log("[Demo] FormAbility onCreate"); + let formData = {}; + return formBindingData.createFormBindingData(formData); + } + + onCastToNormal(formId) { + console.log("[Demo] FormAbility onCastToNormal"); + } + + onUpdate(formId) { + console.log("[Demo] FormAbility onUpdate"); + } + + onVisibilityChange(newStatus) { + console.log("[Demo] FormAbility onVisibilityChange"); + } + + onEvent(formId, message) { + console.log("[Demo] FormAbility onEvent"); + // 在卡片生命周期onEvent用于响应点击事件,可以根据点击事件携带的message拉起不同的ability + // 在这里获取FormExtensionContext,并调用startAbility + let want = { + deviceId: "", + bundleName: "com.example.formstartability", + abilityName: "MainAbility", + }; + this.context.startAbility(want); + } + + onDestroy(formId) { + console.log("[Demo] FormAbility onDestroy"); + } +}; +``` + + + +## 场景用法介绍 + +### 在eTS页面中访问Context + +Stage模型下,在Ability的onWindowStageCreate生命周期中,可以通过WindowStage的SetUIContent方法加载一个eTS页面。在一些场景中,需要在页面内获取Context调用相关API。 + +#### 获取方法 + +在eTS页面中通过以下全局方法获取当前页面关联的Context。 + +| 接口名 | 描述 | +| ------------------------------------- | ---------------------------------------- | +| getContext(component: Object): Object | 获取页面中component所关联的Context对象。 | + +#### 示例代码 + +```js +// MainAbility.ts +import Ability from '@ohos.application.Ability' + +export default class MainAbility extends Ability { + onCreate(want, launchParam) { + console.log("[Demo] MainAbility onCreate"); + } + + onDestroy() { + console.log("[Demo] MainAbility onDestroy"); + } + + onWindowStageCreate(windowStage) { + // 加载index页面,并传入当前Context + windowStage.setUIContent(this.context, "pages/index", null); + } + + onWindowStageDestroy() {} + + onForeground() {} + + onBackground() {} +}; +``` + +```js +// pages/index.ets +import context from '@ohos.application.context' + +type Context = context.Context + +@Entry +@Component +struct Index { + build() { + Row() { + Column() { + Text('GetContext') + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(() => { + // 获取当前component关联的Context + var context : Context = getContext(this) as Context + console.info("CacheDir:" + context.cacheDir) + }) + } + .width('100%') + } + .height('100%') + } +} +``` + + + +### 创建其他应用、其他模块的上下文 + +可以通过createBundleContext和createModuleContext创建其他应用或者其他模块的上下文,然后通过这些Context获取对应的资源信息。具体接口声明详见:[Context接口声明](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/Context.d.ts)。 + +#### 获取方法 + +通过如下接口可以创建指定的Context。 + +| 接口名 | 描述 | +| ------------------------------------------------------------ | -------------------------- | +| createBundleContext(bundleName: string): Context; | 创建指定应用上下文。 | +| createModuleContext(moduleName: string): Context; | 创建指定模块上下文。 | +| createModuleContext(bundleName: string, moduleName: string): Context; | 创建指定应用模块的上下文。 | + +#### 示例代码 + +例如:桌面上显示的应用信息,包括包名以及包的图标,可以通过如下步骤获取: + +1. 创建其他应用的Context +2. 通过Context里面的resourceManager属性拿到应用的资源信息。 + +```js +import Ability from '@ohos.application.Ability' + +export default class MainAbility extends Ability { + onCreate(want, launchParam) { + console.log("[Demo] MainAbility onCreate"); + } + + onDestroy() { + console.log("[Demo] MainAbility onDestroy"); + } + + onWindowStageCreate(windowStage) { + console.log("[Demo] MainAbility onWindowStageCreate"); + + let context = this.context.createBundleContext("com.example.bundle"); + let bundleLabel = context.resourceManager.getString(context.applicationInfo.descriptionId); + console.log("[Demo] MainAbility bundleLabel: " + bundleLabel); + + // 加载index页面,并传入当前Context + windowStage.setUIContent(this.context, "pages/index", null); + } + + onWindowStageDestroy() {} + + onForeground() {} + + onBackground() {} +}; +``` + + + +### 加密分区的使用 + +应用安装的时候,会创建数据目录。数据目录EL1和EL2是不同的加密分区: + +| 分区 | 说明 | +| ---- | -------------------------------------------- | +| EL1 | 设备开机,未解锁时就能够访问的数据区。 | +| EL2 | 设备开机,首次输入密码后才能够访问的数据区。 | + +通过Context的area属性获取和修改当前加密分区。具体接口声明详见:[Context接口声明](https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/Context.d.ts)。 + +#### 示例代码 + +```js +import Ability from '@ohos.application.Ability' + +export default class MainAbility extends Ability { + onCreate(want, launchParam) { + console.log("[Demo] MainAbility onCreate"); + } + + onDestroy() { + console.log("[Demo] MainAbility onDestroy"); + } + + onWindowStageCreate(windowStage) { + console.log("[Demo] MainAbility onWindowStageCreate"); + + // area默认值为EL2,获取到的databaseDir在EL2加密分区下 + console.log("[Demo] MainAbility area: " + this.context.area); + console.log("[Demo] MainAbility databaseDir: " + this.context.databaseDir); + // area修改为EL1,获取到的databaseDir也会变为EL1加密分区下 + this.context.area = 0; + console.log("[Demo] MainAbility databaseDir: " + this.context.databaseDir); + + // 加载index页面,并传入当前Context + windowStage.setUIContent(this.context, "pages/index", null); + } + + onWindowStageDestroy() {} + + onForeground() {} + + onBackground() {} +}; +``` + + + +### 应用开发路径的使用 + +Context提供一些路径供应用开发使用,开发者可以通过访问Context的这些属性获取到对应目录。 + +| 路径 | 作用 | 使用场景 | +| -------------- | ------------------ | ------------------------------------------------------------ | +| cacheDir | 应用的缓存目录 | 通过setting用户可以删除该目录下的数据 | +| tempDir | 存放临时数据的目录 | 该目录应用退出后会被清理。 | +| filesDir | 存放文件数据的目录 | 该目录下存放的文件可能会被应用迁移或备份时同步到其他位置处。 | +| databaseDir | 数据库存储的目录 | | +| preferencesDir | 存储键值对的目录 | | + +不同的上下文获取到的开发路径也存在区别,如ApplicationContext的databaseDir是应用下的目录,而AbilityContext的databaseDir是对应模块下的目录。 + +#### 示例代码 + +```js +import Ability from '@ohos.application.Ability' + +export default class MainAbility extends Ability { + onCreate(want, launchParam) { + console.log("[Demo] MainAbility onCreate"); + } + + onDestroy() { + console.log("[Demo] MainAbility onDestroy"); + } + + onWindowStageCreate(windowStage) { + console.log("[Demo] MainAbility onWindowStageCreate"); + + // 获取AbilityContext的databaseDir目录 + console.log("[Demo] MainAbility databaseDir: " + this.context.databaseDir); + // 获取Application的databaseDir目录 + console.log("[Demo] Application databaseDir: " + this.context.getApplicationContext().databaseDir); + + // 加载index页面,并传入当前Context + windowStage.setUIContent(this.context, "pages/index", null); + } + + onWindowStageDestroy() {} + + onForeground() {} + + onBackground() {} +}; +``` + + + +### Context的EventHub + +通过context.eventHub的事件中心,可以进行事件的订阅、取消订阅以及触发事件。具体接口详见:[EventHub](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-eventhub.md)。 + + + +## 常见问题 + +### 1. Stage模型通过globalThis去获取Context问题 + +#### 现象描述 + +通过globalThis保存和获取Context,使用时发生错误或者崩溃。 + +#### 可能原因 + +应用框架在API9上推出了新的应用模型(Stage模型)。在老的模型(FA模型)下,每个Ability实例有一个js虚拟机实例,所以可以从js引擎的global对象上,获取到一个全局的Ability实例,但是在新的模型(Stage模型)下,整个应用进程共用一个js虚拟机实例,其中可以运行多个Ability实例,这样就不存在一个全局的Ability实例。如果开发者在新的模型(Stage模型)下,调用的API实现仍然走到了获取全局Ability实例的方法,就可能会发生错误或者崩溃。 + +#### 解决办法 + +不要使用globalThis保存Context,参考上述实例中的获取方法章节获取对应的Context。 + +### 2. 一个应用内不同模块共享数据库问题 + +#### 现象描述 + +一个应用内不同模块的ability无法访问到共享的数据库。 + +#### 可能原因 + +在ability内通过context.databaseDir获取到的数据库目录为模块级别的,跨模块时,就无法正确共享数据库了。 + +#### 解决办法 + +使用应用级别的上下文获取数据库目录:context.getApplicationContext().databaseDir。 diff --git a/figures/contextIntroduction.png b/figures/contextIntroduction.png new file mode 100644 index 0000000000000000000000000000000000000000..69a86df94012b0adf3eb0e8c692e4773aba34d06 Binary files /dev/null and b/figures/contextIntroduction.png differ