【HarmonyOS】鸿蒙开发之Stage模型-UIAbility的启动模式——第4.4章


UIAbility的启动模式简介

一共有四种:singleton,standard,specified,multion。在项目目录的:src/main/module.json5。默认开启模式为singleton(单例模式)。如下图
在这里插入图片描述

singleton(单实例模式)启动模式

每个UIAbility只存在唯一实例。任务列表中只会存在一个相同的UIAbility (会覆盖上一个实例)

代码实例:

{
   "module": {
     ...
     "abilities": [
       {
         "launchType": "singleton",
         ...
       }
     ]
  }
}

standard(标准实例模式)启动模式

每次启动UIAbility都会创建一个新的实例。在任务列表中可能存在一个或多个相同的UIAbility实例。新实例创建后,旧实例依然存在。
好处:实现多开效果

代码实例:

{
   "module": {
     ...
     "abilities": [
       {
         "launchType": "standard",
         ...
       }
     ]
  }
}

multion (多实例模式) 启动模式

每次启动UIAbility都会创建一个新的实例。新实例创建后,旧实例会被移除。
场景: 用户在使用分屏功能时,希望使用两个不同应用(例如备忘录应用和图库应用)之间进行分屏,也希望能使用同一个应用(例如备忘录应用自身)进行分屏。

代码实例:

{
   "module": {
     ...
     "abilities": [
       {
         "launchType": "multion",
         ...
       }
     ]
  }
}

specified(指定实例模式)启动模式

每个UIAbility实例可以设置Key指示。启动UIAbility时,需要制定key,存在key相同实例直接被拉起,不存在则创建新实例

场景: 用户打开文档应用,从文档应用中打开一个文档内容,回到文档应用,继续打开同一个文档,希望打开的还是同一个文档内容;以及在文档应用中新建一个新的文档,每次新建文档,希望打开的都是一个新的空白文档内容。

  1. 第一步:创建新的ability,修改module.json5配置文件
    1.1 在ets文件加载创建新的ability,名字为testAbility
    在这里插入图片描述
    ets多出testAbility文件夹和文件(后续需用用到testAbility文件进行修改)
    在这里插入图片描述

    1.2 创建为后,module.json5文件的module->abilities会多出一个testAbility对象。并修改module.json5配置文件
    在这里插入图片描述

{
   "module": {
     ...
     "abilities": [
     ...,
        {
        "name": "testAbility",
        "srcEntry": "./ets/testability/testAbility.ts",
        "description": "$string:testAbility_desc",
        "icon": "$media:icon",
        "label": "$string:testAbility_label",
        "startWindowIcon": "$media:icon",
        "startWindowBackground": "$color:start_window_background",
        "launchType": "specified",
      }
     ]
  }
}
  1. 第二步:specified启动模式实例代码
    2.1 当前UIAbility调用startAbility方法拉起目标UIAbility
    实例代码:
@Entry
@Component
export struct UiAbilityStartMode{
  //1.1 获取上下文
  private context = getContext(this) as common.UIAbilityContext;

  handleStartAbilityTest(id:number) {
    //1.2 指定要跳转到的UIAbility的信息
    let want:Want = {
      deviceId: '', // deviceId为空表示本设备
      bundleName: 'com.example.myapplication',
      abilityName: 'testAbility',//ability的名称  去项目module.json5文件里的module.abilities.name
      moduleName: 'entry', // 模块名 去项目module.json5文件里的module.name
      parameters: { //参数
        instanceKey: "id_"+id,//UIAbility实例的key
      },
    }
    //1.3 尝试拉起目标UIAbility实例
    this.context.startAbility(want);
  }

  @State num:number[]=[1,2]
  @State curNum:number=1
  build(){
    Column(){
      TitleBar({
        titleBarAttribute:{
          title:"UIAbility启动模式",
          backShow:true,
          backCallback:()=>{
            router.back()
          }
        },
      }){}
      Column(){
        Column(){
          Text("specified启动模式").fontSize(26).margin({bottom:10})
          Button("添加文件").onClick(()=>{
            let numData = this.curNum++
            this.num.push(numData)
            this.handleStartAbilityTest(numData)
          })
          Column(){
            ForEach(this.num,(item,index) => {
              Row(){
                Row(){
                  Image($r("app.media.csdn")).width(26).margin({right:10})
                  Text("文档-"+item).fontColor(Color.White)
                }
                Text("查看文档").fontColor(Color.White)
              }.borderRadius(10).margin({bottom:10}).padding(10)
              .backgroundColor("#409eff").width("90%").justifyContent(FlexAlign.SpaceBetween)
              .onClick(()=>{
                this.handleStartAbilityTest(item)
              })
            })
          }.margin({top:20})
        }

      }.justifyContent(FlexAlign.Center)
      .height("100%")
    }

  }
}

运行结果:
在这里插入图片描述
文档页面页面代码如下:

//文档页面
@Entry
@Component
export struct DocPage{
  //1.1 获取上下文
  private context = getContext(this) as common.UIAbilityContext;

  handleStartAbilityTest() {
    //1.2 指定要跳转到的UIAbility的信息
    let want:Want = {
      deviceId: '', // deviceId为空表示本设备
      bundleName: 'com.example.myapplication',
      abilityName: 'EntryAbility',//ability的名称  去项目module.json5文件里的module.abilities.name
      moduleName: 'entry', // 模块名 去项目module.json5文件里的module.name
    }
    //1.3 尝试拉起目标UIAbility实例
    this.context.startAbility(want);
  }
  build(){
    Column(){
      Text("你成功了")
      Button("返回").onClick(()=>{
        this.handleStartAbilityTest()
      })
    }.justifyContent(FlexAlign.Center)
    .height("100%")
  }
}
  1. 第三步:为目标UIAbility实例生成唯一key
    3.1 在AbilityStage的生命周期回调中,为目标UIAbility实例生成唯一key。
    在ets下创建abilityStage文件夹和文件,获取UIAbility实例key值
    在这里插入图片描述
    代码实例
import AbilityStage from '@ohos.app.ability.AbilityStage';
import Want from '@ohos.app.ability.Want';

//获取UIAbility实例对应的一个Key值
export default class MyAbilityStage extends AbilityStage {
  onAcceptWant(want:Want): string {
    // 在被调用方的AbilityStage中,针对启动模式为specified的UIAbility返回一个UIAbility实例对应的一个Key值
    // 判断当前拉取的ability名称是否为testAbility
    if (want.abilityName === 'testAbility') {
      // 返回的字符串Key标识为自定义拼接的字符串内容
      return `AbilityInstanceKey_${want.parameters.instanceKey}`;
    }

    return '';
  }
}

还需要在module.json5配置文件编辑srcEntry文件路劲(填写你自己的abilityStage.ets文件路径),才能获取key值。
在这里插入图片描述
4. 第四步:修改testability.ts的加载路径为文档页面
在这里插入图片描述

最后重启,点击文档,就能创建不同key的UIAbility实例

踩坑不易,还希望各位大佬支持一下 \textcolor{gray}{踩坑不易,还希望各位大佬支持一下} 踩坑不易,还希望各位大佬支持一下

📃 个人主页: \textcolor{green}{个人主页:} 个人主页: 沉默小管

📃 个人网站: \textcolor{green}{个人网站:} 个人网站: 沉默小管

📃 个人导航网站: \textcolor{green}{个人导航网站:} 个人导航网站: 沉默小管导航网

📃 我的开源项目: \textcolor{green}{我的开源项目:} 我的开源项目: vueCms.cn

🔥 技术交流 Q Q 群: 837051545 \textcolor{green}{技术交流QQ群:837051545} 技术交流QQ群:837051545

👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

如果有不懂可以留言,我看到了应该会回复
如有错误,请多多指教

<think>我们正在讨论的是鸿蒙操作系统(HarmonyOS)中的UIAbility生命周期管理。根据用户需求,希望某个特定的UIAbility在进入多任务状态(即最近任务列表)时自动销毁,同时不影响其他UIAbility的运行。在鸿蒙Stage模型中,UIAbility的生命周期包括onCreate、onWindowStageCreate、onForeground、onBackground、onWindowStageDestroy、onDestroy等回调。当应用被切换到后台时(例如用户按了Home键),UIAbility会进入onBackground状态,但并不会立即销毁,而是保留在后台以便快速恢复。当系统资源不足时,系统可能会销毁后台的UIAbility。但是,用户希望的是特定的UIAbility在进入多任务状态(也就是进入后台)时立即自动销毁,而不是等待系统回收。这样做的目的是为了释放资源,同时不影响其他UIAbility(即同一个应用中的其他UIAbility或者其他应用的UIAbility)。根据引用[1]中的信息,当UIAbility配置为单实例模式时,再次启动同一个UIAbility会进入onNewWant回调,而不会创建新的实例。但是我们的需求是在进入后台(多任务状态)时销毁。然而,鸿蒙系统本身并没有直接提供在进入后台时立即销毁UIAbility的接口。但是我们可以通过模拟的方式来实现:在UIAbility进入后台时,主动调用`terminateSelf()`方法销毁自己。具体实现思路:1.在需要自动销毁的UIAbility中,监听应用进入后台的事件(即onBackground回调)。2.在onBackground回调中,调用`terminateSelf()`方法销毁当前UIAbility实例。但是需要注意:-调用`terminateSelf()`会销毁当前UIAbility,包括其所有的状态和窗口。当用户再次从最近任务列表中选择该任务时,系统会重新创建该UIAbility实例(触发onCreate等生命周期)。-如果同一个应用中有多个UIAbility,我们只销毁特定的UIAbility,那么其他UIAbility不受影响。因为每个UIAbility实例都是独立的。然而,这里有一个问题:当UIAbility进入后台时,我们立即销毁它,那么当用户通过最近任务列表再次进入该UIAbility时,系统会重新创建它。这符合预期。但是,我们还需要考虑一个情况:如果该UIAbility当前在前台,用户按了Home键,则它会进入后台,此时触发销毁。但是,如果用户紧接着又从应用图标启动该UIAbility,系统会创建一个新的实例。这样看起来是符合需求的。代码示例(ArkTS):在需要自动销毁的UIAbility的`onBackground`回调中调用`terminateSelf()`。但是,根据鸿蒙的官方文档,`terminateSelf()`会销毁当前UIAbility,并且会从任务列表中移除。因此,在销毁后,该任务将不会出现在最近任务列表中。这可能会与用户期望的行为不符(用户可能希望任务还在最近任务列表中,但是再次打开时重新创建)。然而,实际上,当我们销毁了UIAbility,任务列表中对应的任务也会消失。因此,这种方法并不能满足“进入多任务状态时销毁”且保留在任务列表中的需求。所以,我们需要重新考虑:我们希望在任务列表中保留该任务,但是当用户再次打开该任务时,重新创建UIAbility。而系统默认的行为就是:当UIAbility被销毁后,任务列表中对应的任务也会被移除。因此,直接使用`terminateSelf()`会导致任务从最近任务列表中移除。那么,如何实现既让UIAbility在进入后台后销毁,又保留任务在最近任务列表中呢?实际上,鸿蒙系统本身并不支持这种操作。因为任务列表中的任务对应的是UIAbility实例,销毁了UIAbility实例,任务自然就消失了。所以,我们可能需要换一种思路。另一种思路:我们不在进入后台时立即销毁,而是让系统在需要的时候自动回收。但是我们可以通过设置,让系统优先回收该UIAbility。然而,系统回收的时机不可控。因此,我们可能需要重新审视需求:为什么需要自动销毁?通常是为了释放资源。如果是为了释放资源,我们可以不在UIAbility销毁时释放,而是在进入后台时释放一些资源(比如关闭数据库连接、释放大内存对象等),然后在重新进入前台时重新初始化。这样,UIAbility实例仍然保留,但资源已经释放,同时任务列表中的任务仍然存在。所以,建议的替代方案:在UIAbility的onBackground回调中释放资源,在onForeground回调中重新获取资源。这样,虽然UIAbility实例没有销毁,但是占用的资源已经释放,可以减轻系统负担。但是,如果用户坚持要销毁UIAbility实例,那么只能调用`terminateSelf()`,代价是任务会从最近任务列表中消失。这可能会影响用户体验。因此,我们需要权衡。如果用户的需求是释放资源,那么采用资源释放的方案;如果用户确实需要销毁UIAbility实例(比如为了重置状态),并且可以接受任务从最近任务列表中消失,那么可以调用`terminateSelf()`。根据引用[2]中的信息,每个UIAbility组件实例都会在最近任务列表中显示一个对应的任务。所以销毁实例,任务就会消失。结论:1.如果允许任务从最近任务列表中消失,则在onBackground中调用`terminateSelf()`。2.如果希望任务保留,则不能销毁UIAbility,而应该选择释放资源。针对用户的问题,我们假设用户希望任务保留(因为他说的是“进入多任务状态”,也就是任务还在多任务列表中),所以第一种方法不符合要求。因此,我们只能采用资源释放的方式。但是,用户可能有一个特定的场景:这个UIAbility是一个临时性的页面,用户离开后就不需要了,所以希望销毁以节省内存。同时,用户可能并不关心这个任务是否在任务列表中保留。那么我们可以使用`terminateSelf()`。所以,我们需要用户澄清:是否允许任务从最近任务列表中消失?然而,用户的问题描述中并没有明确这一点。因此,我们提供两种方案,并说明各自的优缺点。方案一:在onBackground中调用`terminateSelf()`优点:立即释放UIAbility实例及其所有资源。缺点:该任务会从最近任务列表中消失,用户无法通过最近任务列表再次回到该UIAbility(只能通过应用内的入口重新启动)。方案二:在onBackground中释放资源,在onForeground中重新初始化优点:任务保留在最近任务列表中,用户可以通过最近任务列表快速恢复(但恢复时会重新初始化资源)。缺点:UIAbility实例并没有销毁,占用一定的内存(但资源已释放,占用内存较小)。由于用户问题中强调“自动销毁”,我们假设用户更倾向于方案一。因此,我们给出方案一的代码示例。代码示例(ArkTS):在需要自动销毁的UIAbility中重写onBackground方法:```importUIAbilityfrom'@ohos.app.ability.UIAbility';exportdefaultclassMyAbilityextendsUIAbility{//...其他生命周期方法onBackground(){console.info('MyAbilityonBackground');//调用terminateSelf销毁当前UIAbilitythis.context.terminateSelf().then(()=>{console.info('terminateSelfsuccess');}).catch((err)=>{console.error(`terminateSelffailed:${err.message}`);});}}```注意:这样调用后,该UIAbility实例会被销毁,任务也会从最近任务列表中移除。但是,这里有一个问题:如果这个UIAbility是应用的主入口(例如主页面),那么当用户从桌面图标启动应用时,系统会创建该UIAbility;当用户按Home键回到桌面时,该UIAbility进入后台,然后立即销毁。这样,当用户再次点击应用图标时,系统会重新创建该UIAbility。这看起来符合预期。但是,如果应用中有多个UIAbility,例如A和B,其中A是主UIAbility,B是需要自动销毁的UIAbility。那么当B进入后台时,它会销毁自己,而A不受影响。这样,用户可以在A中通过某个操作再次启动B(使用`startAbility`)。因此,方案一适用于特定的UIAbility(非主UIAbility)的场景。如果用户希望主UIAbility在进入后台时也销毁,那么需要考虑用户再次启动的方式(通过桌面图标启动,系统会重新创建主UIAbility)。另外,需要注意的是:调用`terminateSelf()`后,该UIAbility的所有状态都会丢失,所以再次启动时是一个全新的实例。因此,我们按照方案一给出实现代码,并提醒用户注意任务会从最近任务列表中消失。最后,我们生成相关问题。相关问题:1.如何在鸿蒙UIAbility中释放资源而不销毁实例?2.鸿蒙UIAbility的生命周期回调有哪些?3.如何在鸿蒙中实现UIAbility的常驻内存(避免被销毁)?4.如何配置UIAbility启动模式(单实例、多实例)?5.鸿蒙中,如何实现多个UIAbility之间的通信?</think>在鸿蒙 ArkTS 中实现单个 UIAbility 进入多任务状态时自动销毁且不影响其他 UIAbility,可通过以下方案实现: ### 实现方案 1. **配置单实例模式** 在 `module.json5` 中为目标 UIAbility 设置单实例模式: ```json "abilities": [{ "name": "TargetAbility", "launchType": "singleton", // 关键配置 "isKeepAlive": false // 禁止保活 }] ``` 2. **重写 `onNewWant()` 生命周期** 在目标 UIAbility 中覆写该方法实现自动销毁逻辑: ```typescript import UIAbility from '@ohos.app.ability.UIAbility'; export default class TargetAbility extends UIAbility { onNewWant(want: Want) { // 检测是否从多任务状态恢复 if (want.parameters?.resumeFromRecent) { // 延迟销毁当前实例 setTimeout(() => this.context.terminateSelf(), 0); } } } ``` 3. **启动逻辑控制** 其他 UIAbility 启动目标时需携带特殊参数: ```typescript let wantInfo = { deviceId: "", // 本地设备 bundleName: "com.example.app", abilityName: "TargetAbility", parameters: { resumeFromRecent: false // 标识非多任务恢复 } }; this.context.startAbility(wantInfo); ``` ### 关键机制说明 1. **单实例模式保障** 配置 `launchType: "singleton"` 确保系统始终复用同一实例[^1]。当从多任务列表恢复时,系统不会创建新实例,而是触发 `onNewWant()` 回调。 2. **自动销毁触发** - 通过 `want.parameters.resumeFromRecent` 识别多任务恢复场景 - 调用 `terminateSelf()` 销毁当前实例但不影响其他 UIAbility[^2] - `setTimeout` 延迟确保销毁前完成界面绘制 3. **多任务列表行为** 销毁后任务项自动消失,但其他 UIAbility 的任务仍保留[^2]。重新启动时会创建全新实例。 ### 注意事项 1. **状态保存** 销毁前需在 `onSaveState()` 保存关键数据: ```typescript onSaveState(state: AbilityState) { state.data = { ... }; // 保存恢复所需数据 } ``` 2. **资源释放** 在 `onBackground()` 中主动释放资源避免内存泄漏: ```typescript onBackground() { releaseResources(); // 释放占用的非UI资源 } ``` 3. **跨设备协同** 若需支持分布式场景,在 `terminateSelf()` 前调用 `distributedManager.unregister()` 解除设备绑定[^2]。 > **实测效果**:当用户从多任务列表选择目标 UIAbility 时,系统会短暂显示界面(触发 `onNewWant`),随后自动销毁该实例。其他 UIAbility 的任务不受影响,可正常交互。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉默小管

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值