ArkTS 中单实例模式和多实例模式的区别

📌 快速概览

在 HarmonyOS ArkTS 中,UIAbility 支持两种启动模式:单实例模式(singleton)多实例模式(multiton)。这两种模式决定了应用在运行时如何管理 UIAbility 实例。


🎯 一、核心概念

1.1 什么是 UIAbility 启动模式?

UIAbility 启动模式:指定 UIAbility 在被启动时如何创建和管理实例的策略。

两种模式

启动模式 英文名 说明
单实例模式 singleton 系统中只存在一个 UIAbility 实例
多实例模式 multiton 每次启动都会创建一个新的 UIAbility 实例
指定实例模式 specified 根据业务需要创建多个命名实例(高级用法)

注意:本文档主要介绍单实例模式多实例模式


1.2 配置位置

module.json5 文件中配置:

{
  "module": {
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "launchType": "singleton", // 或 "multiton"
        "description": "$string:EntryAbility_desc",
        "icon": "$media:icon",
        "label": "$string:EntryAbility_label",
        "exported": true
      }
    ]
  }
}

关键字段

  • launchType: 启动模式
    • "singleton": 单实例模式(默认)
    • "multiton": 多实例模式
    • "specified": 指定实例模式

📖 二、单实例模式(singleton)

2.1 核心特点

定义:系统中只会存在一个 UIAbility 实例。

行为

  • 首次启动:创建新的 UIAbility 实例
  • 再次启动:复用已存在的实例,不会创建新实例
  • 回调触发:调用 onNewWant() 回调,而不是 onCreate()

2.2 生命周期流程

首次启动
启动 UIAbility
   ↓
onCreate()        // 创建实例
   ↓
onWindowStageCreate()  // 创建窗口
   ↓
onForeground()    // 进入前台
   ↓
显示页面
再次启动(已有实例)
启动 UIAbility
   ↓
onNewWant()       // 收到新的启动请求
   ↓
onForeground()    // 进入前台(如果在后台)
   ↓
显示页面

2.3 代码示例

配置单实例模式
// module.json5
{
  "module": {
    "abilities": [
      {
        "name": "MainAbility",
        "launchType": "singleton", // 单实例模式
        "srcEntry": "./ets/entryability/MainAbility.ets"
      }
    ]
  }
}
UIAbility 实现
// MainAbility.ets
import UIAbility from "@ohos.app.ability.UIAbility";
import window from "@ohos.window";
import Want from "@ohos.app.ability.Want";

export default class MainAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    console.info("[MainAbility] onCreate - 首次创建");
    console.info("Want:", JSON.stringify(want));
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    console.info("[MainAbility] onNewWant - 再次启动(复用实例)");
    console.info("新的 Want:", JSON.stringify(want));

    // 处理新的启动参数
    if (want.parameters) {
      const params = want.parameters as Record<string, string>;
      const page = params.page || "pages/Index";

      // 跳转到指定页面
      this.context.windowStage
        ?.getMainWindow()
        .then((windowObj: window.Window) => {
          windowObj.loadContent(page);
        });
    }
  }

  onForeground() {
    console.info("[MainAbility] onForeground - 进入前台");
  }

  onBackground() {
    console.info("[MainAbility] onBackground - 进入后台");
  }

  onDestroy() {
    console.info("[MainAbility] onDestroy - 销毁");
  }
}

2.4 启动单实例 UIAbility

// 首次启动
import common from "@ohos.app.ability.common";

// 获取 context
let context = getContext(this) as common.UIAbilityContext;

// 启动 UIAbility
context
  .startAbility({
    bundleName: "com.example.myapp",
    abilityName: "MainAbility",
    parameters: {
      page: "pages/Index",
    },
  })
  .then(() => {
    console.info("启动成功");
  })
  .catch((error: Error) => {
    console.error("启动失败:", error.message);
  });

// 再次启动(会触发 onNewWant)
context.startAbility({
  bundleName: "com.example.myapp",
  abilityName: "MainAbility",
  parameters: {
    page: "pages/Detail",
    id: "123",
  },
});

2.5 使用场景

✅ 适合场景

  1. 主页面

    • 应用的入口页面
    • 首页、主界面
  2. 单一功能页面

    • 设置页面
    • 关于页面
    • 帮助页面
  3. 全局状态管理

    • 需要保持全局状态的页面
    • 音乐播放器、下载管理器
  4. 避免重复创建

    • 内存占用大的页面
    • 需要保持数据连续性的页面

示例

// 音乐播放器(单实例)
@Entry
@Component
struct MusicPlayer {
  @State currentSong: Song = null
  @State isPlaying: boolean = false

  build() {
    Column() {
      Text(`正在播放: ${this.currentSong?.name}`)

      Row() {
        Button('上一曲').onClick(() => this.previous())
        Button(this.isPlaying ? '暂停' : '播放').onClick(() => this.togglePlay())
        Button('下一曲').onClick(() => this.next())
      }
    }
  }
}

📖 三、多实例模式(multiton)

3.1 核心特点

定义:每次启动都会创建新的 UIAbility 实例。

行为

  • 每次启动:都会创建新的 UIAbility 实例
  • 独立生命周期:每个实例有自己独立的生命周期
  • 不会调用 onNewWant():每次都调用 onCreate()

3.2 生命周期流程

每次启动
启动 UIAbility
   ↓
onCreate()        // 创建新实例
   ↓
onWindowStageCreate()  // 创建窗口
   ↓
onForeground()    // 进入前台
   ↓
显示页面
多个实例共存
实例 1              实例 2              实例 3
  ↓                  ↓                  ↓
onCreate()         onCreate()         onCreate()
  ↓                  ↓                  ↓
onForeground()     onForeground()     onForeground()
  ↓                  ↓                  ↓
显示页面            显示页面            显示页面

3.3 代码示例

配置多实例模式
// module.json5
{
  "module": {
    "abilities": [
      {
        "name": "DocumentAbility",
        "launchType": "multiton", // 多实例模式
        "srcEntry": "./ets/entryability/DocumentAbility.ets"
      }
    ]
  }
}
UIAbility 实现
// DocumentAbility.ets
import UIAbility from "@ohos.app.ability.UIAbility";
import window from "@ohos.window";
import Want from "@ohos.app.ability.Want";

export default class DocumentAbility extends UIAbility {
  private instanceId: string = "";

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // 每次启动都会调用
    this.instanceId = `instance_${Date.now()}`;
    console.info(`[DocumentAbility] onCreate - 创建新实例: ${this.instanceId}`);
    console.info("Want:", JSON.stringify(want));

    // 获取文档 ID
    if (want.parameters) {
      const params = want.parameters as Record<string, string>;
      const docId = params.docId;
      console.info(`打开文档: ${docId}`);
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    console.info(`[DocumentAbility] onWindowStageCreate - ${this.instanceId}`);

    windowStage.loadContent("pages/DocumentEditor", (err, data) => {
      if (err.code) {
        console.error("加载页面失败:", err.message);
        return;
      }
      console.info("加载页面成功");
    });
  }

  onForeground() {
    console.info(`[DocumentAbility] onForeground - ${this.instanceId}`);
  }

  onBackground() {
    console.info(`[DocumentAbility] onBackground - ${this.instanceId}`);
  }

  onDestroy() {
    console.info(`[DocumentAbility] onDestroy - ${this.instanceId}`);
  }
}

3.4 启动多实例 UIAbility

// 启动多个文档编辑器实例
import common from "@ohos.app.ability.common";

let context = getContext(this) as common.UIAbilityContext;

// 打开文档 1
context.startAbility({
  bundleName: "com.example.myapp",
  abilityName: "DocumentAbility",
  parameters: {
    docId: "doc_001",
    docName: "项目方案.docx",
  },
});

// 打开文档 2(创建新实例)
context.startAbility({
  bundleName: "com.example.myapp",
  abilityName: "DocumentAbility",
  parameters: {
    docId: "doc_002",
    docName: "会议纪要.docx",
  },
});

// 打开文档 3(创建新实例)
context.startAbility({
  bundleName: "com.example.myapp",
  abilityName: "DocumentAbility",
  parameters: {
    docId: "doc_003",
    docName: "技术文档.docx",
  },
});

结果

  • 创建了 3 个独立的 DocumentAbility 实例
  • 每个实例显示不同的文档
  • 用户可以在 3 个文档之间切换

3.5 使用场景

✅ 适合场景

  1. 文档编辑器

    • 同时打开多个文档
    • 每个文档独立编辑
  2. 浏览器标签页

    • 每个标签页是一个实例
    • 独立的浏览历史
  3. 聊天窗口

    • 同时和多个好友聊天
    • 每个聊天窗口独立
  4. 多任务处理

    • 需要并行处理多个任务
    • 每个任务独立管理

示例

// 文档编辑器(多实例)
@Entry
@Component
struct DocumentEditor {
  @State documentId: string = ''
  @State content: string = ''

  aboutToAppear() {
    // 加载文档内容
    const params = router.getParams() as Record<string, string>
    this.documentId = params.docId
    this.loadDocument(this.documentId)
  }

  loadDocument(docId: string) {
    // 从数据库或网络加载文档
    console.info(`加载文档: ${docId}`)
  }

  build() {
    Column() {
      Text(`文档 ID: ${this.documentId}`)
        .fontSize(16)

      TextArea({ text: this.content })
        .onChange((value: string) => {
          this.content = value
        })

      Row() {
        Button('保存').onClick(() => this.save())
        Button('关闭').onClick(() => this.close())
      }
    }
  }

  save() {
    console.info('保存文档')
  }

  close() {
    // 关闭当前实例
    const context = getContext(this) as common.UIAbilityContext
    context.terminateSelf()
  }
}

📊 四、两种模式对比

4.1 核心区别

对比项 单实例模式(singleton) 多实例模式(multiton)
实例数量 只有 1 个 可以有多个
首次启动 调用 onCreate() 调用 onCreate()
再次启动 调用 onNewWant() 调用 onCreate()(新实例)
内存占用 较小(只有一个实例) 较大(多个实例)
数据隔离 无(共享同一实例) 完全隔离(不同实例)
性能 启动快(复用实例) 启动慢(创建新实例)
适用场景 主页、设置、全局功能 文档编辑、浏览器、聊天窗口
配置值 "launchType": "singleton" "launchType": "multiton"
默认模式 ✅ 是(默认) ❌ 否

4.2 生命周期对比

单实例模式
第 1 次启动:
onCreate() → onWindowStageCreate() → onForeground()

第 2 次启动(已有实例):
onNewWant() → onForeground()

第 3 次启动(已有实例):
onNewWant() → onForeground()
多实例模式
第 1 次启动:
实例 1: onCreate() → onWindowStageCreate() → onForeground()

第 2 次启动:
实例 2: onCreate() → onWindowStageCreate() → onForeground()

第 3 次启动:
实例 3: onCreate() → onWindowStageCreate() → onForeground()

4.3 内存和性能对比

指标 单实例模式 多实例模式
内存占用 低(1 个实例) 高(多个实例)
启动速度 快(复用实例) 慢(创建新实例)
切换速度 快(无需创建) 中等(已创建实例)
资源消耗
数据持久性 高(实例一直存在) 低(实例可能被销毁)
并发能力 低(只有一个实例) 高(多个实例并行)

🎯 五、实际应用场景

5.1 场景 1:主应用(单实例)

需求:应用的主页面,用户总是返回到同一个首页。

配置

{
  "name": "MainAbility",
  "launchType": "singleton",
  "srcEntry": "./ets/entryability/MainAbility.ets"
}

代码

// MainAbility.ets
export default class MainAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    console.info("[MainAbility] onCreate - 应用启动");
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    console.info("[MainAbility] onNewWant - 返回应用");

    // 处理启动参数(如通知点击、分享等)
    if (want.parameters) {
      const params = want.parameters as Record<string, string>;

      // 根据参数跳转到不同页面
      if (params.action === "notification") {
        this.navigateToNotification(params.notificationId);
      } else if (params.action === "share") {
        this.navigateToShare(params.shareData);
      }
    }
  }

  navigateToNotification(notificationId: string) {
    // 跳转到通知详情页
    router.pushUrl({
      url: "pages/NotificationDetail",
      params: { id: notificationId },
    });
  }

  navigateToShare(shareData: string) {
    // 跳转到分享页面
    router.pushUrl({
      url: "pages/Share",
      params: { data: shareData },
    });
  }
}

5.2 场景 2:文档编辑器(多实例)

需求:用户可以同时打开多个文档进行编辑。

配置

{
  "name": "DocumentAbility",
  "launchType": "multiton",
  "srcEntry": "./ets/entryability/DocumentAbility.ets"
}

代码

// DocumentAbility.ets
export default class DocumentAbility extends UIAbility {
  private documentId: string = "";
  private documentData: DocumentData | null = null;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    console.info("[DocumentAbility] onCreate - 打开新文档");

    // 获取文档 ID
    if (want.parameters) {
      const params = want.parameters as Record<string, string>;
      this.documentId = params.docId || "";

      // 加载文档数据
      this.loadDocument(this.documentId);
    }
  }

  async loadDocument(docId: string) {
    try {
      // 从数据库或网络加载文档
      const doc = await DocumentService.getDocument(docId);
      this.documentData = doc;

      console.info(`文档加载成功: ${doc.name}`);
    } catch (error) {
      console.error("文档加载失败:", error.message);
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    // 加载文档编辑器页面
    windowStage.loadContent("pages/DocumentEditor", (err, data) => {
      if (err.code) {
        console.error("加载页面失败");
        return;
      }

      // 将文档数据传递给页面
      AppStorage.SetOrCreate("currentDocument", this.documentData);
    });
  }

  onDestroy() {
    console.info(`[DocumentAbility] onDestroy - 关闭文档: ${this.documentId}`);

    // 清理资源
    this.documentData = null;
  }
}

页面代码

// pages/DocumentEditor.ets
@Entry
@Component
struct DocumentEditor {
  @StorageLink('currentDocument') document: DocumentData = null
  @State content: string = ''
  @State isSaved: boolean = true

  aboutToAppear() {
    if (this.document) {
      this.content = this.document.content
    }
  }

  build() {
    Column() {
      // 文档标题
      Row() {
        Text(this.document?.name || '未命名文档')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)

        Text(this.isSaved ? '已保存' : '未保存')
          .fontSize(14)
          .fontColor(this.isSaved ? Color.Green : Color.Orange)
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)
      .padding(15)

      // 文档内容编辑器
      TextArea({ text: this.content })
        .width('100%')
        .height('80%')
        .onChange((value: string) => {
          this.content = value
          this.isSaved = false
        })

      // 操作按钮
      Row() {
        Button('保存')
          .onClick(() => this.saveDocument())

        Button('另存为')
          .onClick(() => this.saveAs())

        Button('关闭')
          .onClick(() => this.closeDocument())
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceEvenly)
      .padding(15)
    }
    .width('100%')
    .height('100%')
  }

  async saveDocument() {
    try {
      await DocumentService.saveDocument(this.document.id, this.content)
      this.isSaved = true
      promptAction.showToast({ message: '保存成功' })
    } catch (error) {
      promptAction.showToast({ message: '保存失败' })
    }
  }

  saveAs() {
    // 另存为逻辑
    console.info('另存为')
  }

  closeDocument() {
    if (!this.isSaved) {
      // 提示保存
      AlertDialog.show({
        title: '提示',
        message: '文档未保存,是否保存?',
        primaryButton: {
          value: '保存',
          action: () => {
            this.saveDocument().then(() => {
              this.terminate()
            })
          }
        },
        secondaryButton: {
          value: '不保存',
          action: () => {
            this.terminate()
          }
        }
      })
    } else {
      this.terminate()
    }
  }

  terminate() {
    const context = getContext(this) as common.UIAbilityContext
    context.terminateSelf()
  }
}

// 文档数据模型
class DocumentData {
  id: string
  name: string
  content: string
  createTime: Date
  updateTime: Date

  constructor(id: string, name: string, content: string) {
    this.id = id
    this.name = name
    this.content = content
    this.createTime = new Date()
    this.updateTime = new Date()
  }
}

// 文档服务(模拟)
class DocumentService {
  static async getDocument(docId: string): Promise<DocumentData> {
    // 模拟网络请求
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(new DocumentData(docId, `文档_${docId}`, '这是文档内容...'))
      }, 500)
    })
  }

  static async saveDocument(docId: string, content: string): Promise<void> {
    // 模拟保存
    return new Promise((resolve) => {
      setTimeout(() => {
        console.info(`保存文档 ${docId}`)
        resolve()
      }, 300)
    })
  }
}

5.3 场景 3:浏览器(多实例)

需求:用户可以打开多个浏览器标签页。

配置

{
  "name": "BrowserAbility",
  "launchType": "multiton",
  "srcEntry": "./ets/entryability/BrowserAbility.ets"
}

代码

// BrowserAbility.ets
export default class BrowserAbility extends UIAbility {
  private tabId: string = ''
  private url: string = ''

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    this.tabId = `tab_${Date.now()}`

    if (want.parameters) {
      const params = want.parameters as Record<string, string>
      this.url = params.url || 'https://www.example.com'
    }

    console.info(`[BrowserAbility] 打开新标签页: ${this.tabId}, URL: ${this.url}`)
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/BrowserPage', (err, data) => {
      if (!err.code) {
        // 传递 URL 给页面
        AppStorage.SetOrCreate(`tab_${this.tabId}`, this.url)
      }
    })
  }
}

// pages/BrowserPage.ets
@Entry
@Component
struct BrowserPage {
  @State currentUrl: string = 'https://www.example.com'
  @State canGoBack: boolean = false
  @State canGoForward: boolean = false

  build() {
    Column() {
      // 地址栏
      Row() {
        Button('后退')
          .enabled(this.canGoBack)
          .onClick(() => this.goBack())

        Button('前进')
          .enabled(this.canGoForward)
          .onClick(() => this.goForward())

        TextInput({ text: this.currentUrl })
          .layoutWeight(1)
          .onSubmit(() => this.navigate(this.currentUrl))

        Button('刷新')
          .onClick(() => this.refresh())
      }
      .width('100%')
      .padding(10)

      // 网页内容
      Web({ src: this.currentUrl, controller: this.webController })
        .width('100%')
        .layoutWeight(1)
        .onPageEnd(() => {
          this.updateNavigationState()
        })
    }
    .width('100%')
    .height('100%')
  }

  private webController = new WebController()

  goBack() {
    this.webController.backward()
  }

  goForward() {
    this.webController.forward()
  }

  refresh() {
    this.webController.refresh()
  }

  navigate(url: string) {
    this.webController.loadUrl(url)
  }

  updateNavigationState() {
    this.canGoBack = this.webController.accessBackward()
    this.canGoForward = this.webController.accessForward()
    this.currentUrl = this.webController.getUrl()
  }
}

⚠️ 六、注意事项

6.1 单实例模式注意事项

1. 必须处理 onNewWant()
// ❌ 错误:没有处理 onNewWant
export default class MainAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // 首次启动的逻辑
  }

  // ❌ 缺少 onNewWant,再次启动时无法处理新参数
}

// ✅ 正确:处理 onNewWant
export default class MainAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    this.handleWant(want);
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // ✅ 处理再次启动的逻辑
    this.handleWant(want);
  }

  handleWant(want: Want) {
    // 统一处理启动参数
    if (want.parameters) {
      const params = want.parameters as Record<string, string>;
      this.navigateToPage(params.page);
    }
  }
}

2. 状态管理
// 单实例模式下,状态会一直保持
export default class MainAbility extends UIAbility {
  private userInfo: UserInfo | null = null;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // 首次启动时加载用户信息
    this.loadUserInfo();
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // ⚠️ 注意:userInfo 仍然存在,不会重新创建
    console.info("用户信息:", this.userInfo);

    // 如果需要刷新数据,需要显式调用
    if (want.parameters?.refresh === "true") {
      this.loadUserInfo();
    }
  }

  async loadUserInfo() {
    this.userInfo = await UserService.getUserInfo();
  }
}

6.2 多实例模式注意事项

1. 内存管理
// ⚠️ 多实例会占用更多内存
// 需要及时关闭不需要的实例

@Component
struct DocumentEditor {
  build() {
    Column() {
      Button('关闭文档')
        .onClick(() => {
          // ✅ 关闭当前实例,释放内存
          const context = getContext(this) as common.UIAbilityContext
          context.terminateSelf()
        })
    }
  }
}

2. 数据隔离
// 每个实例的数据是隔离的
export default class DocumentAbility extends UIAbility {
  private documentId: string = ""; // 每个实例独立
  private content: string = ""; // 每个实例独立

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // ✅ 每个实例有自己的 documentId 和 content
    this.documentId = want.parameters?.docId as string;
    this.loadDocument(this.documentId);
  }
}

// ⚠️ 如果需要跨实例共享数据,使用 AppStorage 或数据库
AppStorage.SetOrCreate("sharedData", { key: "value" });

3. 实例限制
// ⚠️ 系统对实例数量有限制,不要无限创建实例

// 建议:维护一个实例管理器
class InstanceManager {
  private static instances: Set<string> = new Set()
  private static MAX_INSTANCES = 5

  static canCreateInstance(): boolean {
    return this.instances.size < this.MAX_INSTANCES
  }

  static addInstance(instanceId: string) {
    this.instances.add(instanceId)
  }

  static removeInstance(instanceId: string) {
    this.instances.delete(instanceId)
  }
}

// 在启动前检查
if (InstanceManager.canCreateInstance()) {
  context.startAbility({...})
} else {
  promptAction.showToast({ message: '打开的文档数量已达上限' })
}

🎯 七、最佳实践

7.1 选择启动模式的决策树

需要同时存在多个独立实例?
├── 是 → 使用多实例模式(multiton)
│   ├── 文档编辑器
│   ├── 浏览器标签页
│   ├── 聊天窗口
│   └── 多任务处理
│
└── 否 → 使用单实例模式(singleton)
    ├── 应用主页
    ├── 设置页面
    ├── 音乐播放器
    └── 全局功能

7.2 性能优化建议

1. 单实例模式
// ✅ 缓存数据,避免重复加载
export default class MainAbility extends UIAbility {
  private cachedData: Map<string, any> = new Map();

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    this.loadInitialData();
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // ✅ 使用缓存数据
    const params = want.parameters as Record<string, string>;
    const page = params.page;

    if (this.cachedData.has(page)) {
      console.info("使用缓存数据");
      this.navigateToPage(page, this.cachedData.get(page));
    } else {
      console.info("加载新数据");
      this.loadPageData(page);
    }
  }
}

2. 多实例模式
// ✅ 延迟加载,减少启动时间
export default class DocumentAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // ✅ 先显示界面,后加载数据
    this.showLoadingUI();

    // 异步加载数据
    this.loadDocumentAsync(want.parameters?.docId as string);
  }

  async loadDocumentAsync(docId: string) {
    try {
      const doc = await DocumentService.getDocument(docId);
      this.updateUI(doc);
    } catch (error) {
      this.showError(error.message);
    }
  }
}

7.3 混合使用

// 应用可以同时使用两种模式
// module.json5
{
  "module": {
    "abilities": [
      {
        "name": "MainAbility",
        "launchType": "singleton",  // 主页面:单实例
        "srcEntry": "./ets/entryability/MainAbility.ets"
      },
      {
        "name": "DocumentAbility",
        "launchType": "multiton",   // 文档编辑:多实例
        "srcEntry": "./ets/entryability/DocumentAbility.ets"
      },
      {
        "name": "SettingsAbility",
        "launchType": "singleton",  // 设置页面:单实例
        "srcEntry": "./ets/entryability/SettingsAbility.ets"
      }
    ]
  }
}

🎓 八、快速参考

8.1 配置速查

// 单实例模式
{
  "name": "MyAbility",
  "launchType": "singleton"
}

// 多实例模式
{
  "name": "MyAbility",
  "launchType": "multiton"
}

8.2 生命周期回调速查

回调方法 单实例模式 多实例模式 说明
onCreate() 首次启动 每次启动 创建实例
onNewWant() 再次启动 ❌ 不调用 复用实例时调用
onWindowStageCreate() 首次启动 每次启动 创建窗口
onForeground() 每次前台 每次前台 进入前台
onBackground() 每次后台 每次后台 进入后台
onDestroy() 应用退出 实例关闭 销毁实例

8.3 常见问题速查

问题 解决方案
如何在单实例中处理新的启动请求? 实现 onNewWant() 回调
如何关闭多实例中的某个实例? 调用 context.terminateSelf()
如何限制多实例的数量? 维护实例计数器,启动前检查
如何在实例间共享数据? 使用 AppStorage 或持久化存储
如何判断当前使用的是哪种模式? 查看 module.json5 中的配置

📌 九、总结

核心要点

  1. 单实例模式:系统只有一个实例,适合主页、设置等场景
  2. 多实例模式:可以创建多个实例,适合文档编辑、浏览器等场景
  3. 关键区别:单实例会调用 onNewWant(),多实例每次都调用 onCreate()
  4. 性能考虑:单实例启动快但灵活性低,多实例灵活但占用内存多
  5. 混合使用:一个应用可以同时使用两种模式

选择建议

场景 推荐模式
应用主页 单实例
设置页面 单实例
音乐播放器 单实例
下载管理器 单实例
文档编辑器 多实例
浏览器标签页 多实例
聊天窗口 多实例
图片查看器 多实例

记忆口诀

单实例省内存,复用快又稳
多实例能并行,独立又灵活
主页用单例,文档用多例
根据需求选,合理配置好

完! 🚀

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐