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、测试、元服务和应用上架分发等。

更多推荐