HarmonyOS 鸿蒙Next中如何实现应用间通信?

HarmonyOS 鸿蒙Next中如何实现应用间通信? 1.如何使用Want传递数据、调用其他应用功能和实现跨应用数据共享? 2.在HarmonyOS中如何实现应用间通信?

3 回复

解决方案

1. 显式Want启动Ability

import common from '@ohos.app.ability.common'
import Want from '@ohos.app.ability.Want'
import { BusinessError } from '@ohos.base'

@Entry
@Component
struct ExplicitWant {
  private context = getContext(this) as common.UIAbilityContext

  build() {
    Column({ space: 16 }) {
      Text('显式启动Ability')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Button('启动本应用其他页面')
        .width('100%')
        .onClick(() => {
          this.startOtherAbility()
        })

      Button('启动并传递参数')
        .width('100%')
        .onClick(() => {
          this.startAbilityWithData()
        })

      Button('启动并等待结果')
        .width('100%')
        .onClick(() => {
          this.startAbilityForResult()
        })
    }
    .padding(16)
  }

  private async startOtherAbility() {
    try {
      const want: Want = {
        deviceId: '', // 空表示本设备
        bundleName: 'com.example.myapp',
        abilityName: 'EntryAbility',
        moduleName: 'entry'
      }

      await this.context.startAbility(want)
      console.log('Ability启动成功')
    } catch (error) {
      const err = error as BusinessError
      console.error('启动Ability失败:', err.message)
    }
  }

  private async startAbilityWithData() {
    try {
      const want: Want = {
        deviceId: '',
        bundleName: 'com.example.myapp',
        abilityName: 'DetailAbility',
        moduleName: 'entry',
        parameters: {
          // 传递的数据
          id: 123,
          title: '标题',
          content: '这是详情内容',
          timestamp: Date.now(),
          // 可以传递复杂对象
          user: {
            name: '张三',
            age: 25
          },
          // 数组
          tags: ['技术', 'HarmonyOS', '开发']
        }
      }

      await this.context.startAbility(want)
      console.log('带参数的Ability启动成功')
    } catch (error) {
      const err = error as BusinessError
      console.error('启动失败:', err.message)
    }
  }

  private async startAbilityForResult() {
    try {
      const want: Want = {
        deviceId: '',
        bundleName: 'com.example.myapp',
        abilityName: 'SelectAbility',
        moduleName: 'entry',
        parameters: {
          type: 'single',
          title: '请选择'
        }
      }

      // 启动并等待返回结果
      const result = await this.context.startAbilityForResult(want)
      
      // 处理返回的数据
      console.log('返回结果:', result.resultCode)
      console.log('返回数据:', result.want?.parameters)
      
      if (result.resultCode === 0) {
        const selectedItem = result.want?.parameters?.['selected']
        console.log('选择的项目:', selectedItem)
      }
    } catch (error) {
      const err = error as BusinessError
      console.error('启动失败:', err.message)
    }
  }
}

// 接收参数的Ability示例
// EntryAbility.ets
import UIAbility from '@ohos.app.ability.UIAbility'
import Want from '@ohos.app.ability.Want'
import window from '@ohos.window'

export default class DetailAbility extends UIAbility {
  onCreate(want: Want) {
    // 接收传递的参数
    const id = want.parameters?.['id'] as number
    const title = want.parameters?.['title'] as string
    const user = want.parameters?.['user'] as Record<string, Object>
    
    console.log('接收到参数:', id, title, user)
    
    // 可以将参数保存到AppStorage供页面使用
    AppStorage.setOrCreate('detailId', id)
    AppStorage.setOrCreate('detailTitle', title)
  }

  onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/DetailPage', (err) => {
      if (err.code) {
        console.error('加载页面失败')
        return
      }
      console.log('页面加载成功')
    })
  }
}

2. 隐式Want调用其他应用

import common from '@ohos.app.ability.common'
import Want from '@ohos.app.ability.Want'

@Entry
@Component
struct ImplicitWant {
  private context = getContext(this) as common.UIAbilityContext

  build() {
    Column({ space: 16 }) {
      Text('隐式调用其他应用')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Button('拨打电话')
        .width('100%')
        .onClick(() => {
          this.makePhoneCall('10086')
        })

      Button('发送短信')
        .width('100%')
        .onClick(() => {
          this.sendSMS('10086', '测试短信')
        })

      Button('打开网页')
        .width('100%')
        .onClick(() => {
          this.openBrowser('https://www.harmonyos.com')
        })

      Button('查看图片')
        .width('100%')
        .onClick(() => {
          this.viewImage()
        })

      Button('分享文本')
        .width('100%')
        .onClick(() => {
          this.shareText('分享的内容')
        })
    }
    .padding(16)
  }

  private async makePhoneCall(phoneNumber: string) {
    try {
      const want: Want = {
        action: 'ohos.want.action.dial',
        uri: `tel:${phoneNumber}`
      }

      await this.context.startAbility(want)
    } catch (error) {
      const err = error as BusinessError
      console.error('拨打电话失败:', err.message)
    }
  }

  private async sendSMS(phoneNumber: string, message: string) {
    try {
      const want: Want = {
        action: 'ohos.want.action.sendto',
        uri: `sms:${phoneNumber}`,
        parameters: {
          content: message
        }
      }

      await this.context.startAbility(want)
    } catch (error) {
      const err = error as BusinessError
      console.error('发送短信失败:', err.message)
    }
  }

  private async openBrowser(url: string) {
    try {
      const want: Want = {
        action: 'ohos.want.action.viewData',
        uri: url,
        type: 'text/html'
      }

      await this.context.startAbility(want)
    } catch (error) {
      const err = error as BusinessError
      console.error('打开浏览器失败:', err.message)
    }
  }

  private async viewImage() {
    try {
      const want: Want = {
        action: 'ohos.want.action.viewData',
        uri: 'file:///data/storage/el2/base/haps/entry/files/image.jpg',
        type: 'image/*'
      }

      await this.context.startAbility(want)
    } catch (error) {
      const err = error as BusinessError
      console.error('查看图片失败:', err.message)
    }
  }

  private async shareText(text: string) {
    try {
      const want: Want = {
        action: 'ohos.want.action.sendData',
        type: 'text/plain',
        parameters: {
          'ability.want.params.CONTENT': text
        }
      }

      await this.context.startAbility(want)
    } catch (error) {
      const err = error as BusinessError
      console.error('分享失败:', err.message)
    }
  }
}

3. 接收Want并返回结果

// SelectAbility.ets - 选择器Ability
import UIAbility from '@ohos.app.ability.UIAbility'
import Want from '@ohos.app.ability.Want'
import AbilityConstant from '@ohos.app.ability.AbilityConstant'

export default class SelectAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // 保存请求参数
    const type = want.parameters?.['type'] as string
    const title = want.parameters?.['title'] as string
    
    AppStorage.setOrCreate('selectType', type)
    AppStorage.setOrCreate('selectTitle', title)
  }

  // 返回结果给调用方
  returnResult(selectedItem: string) {
    const want: Want = {
      parameters: {
        selected: selectedItem
      }
    }

    // 设置返回结果
    this.context.terminateSelfWithResult({
      resultCode: 0, // 0表示成功
      want: want
    })
  }

  // 取消操作
  cancel() {
    this.context.terminateSelfWithResult({
      resultCode: -1, // -1表示取消
      want: {}
    })
  }
}

// SelectPage.ets - 选择器页面
@Entry
@Component
struct SelectPage {
  @StorageLink('selectType') selectType: string = ''
  @StorageLink('selectTitle') selectTitle: string = ''
  @State items: string[] = ['选项1', '选项2', '选项3']
  private context = getContext(this) as common.UIAbilityContext

  build() {
    Column({ space: 16 }) {
      Text(this.selectTitle)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      List() {
        ForEach(this.items, (item: string) => {
          ListItem() {
            Text(item)
              .width('100%')
              .padding(16)
              .onClick(() => {
                this.selectItem(item)
              })
          }
        })
      }
      .layoutWeight(1)
      .divider({ strokeWidth: 1, color: '#f0f0f0' })

      Button('取消')
        .width('100%')
        .onClick(() => {
          this.cancel()
        })
    }
    .padding(16)
  }

  private selectItem(item: string) {
    const want: Want = {
      parameters: {
        selected: item
      }
    }

    this.context.terminateSelfWithResult({
      resultCode: 0,
      want: want
    })
  }

  private cancel() {
    this.context.terminateSelfWithResult({
      resultCode: -1,
      want: {}
    })
  }
}

4. Want工具类

// utils/WantUtil.ets
import common from '@ohos.app.ability.common'
import Want from '@ohos.app.ability.Want'
import { BusinessError } from '@ohos.base'

export class WantUtil {
  /**
   * 启动Ability
   */
  static async startAbility(
    context: common.UIAbilityContext,
    bundleName: string,
    abilityName: string,
    parameters?: Record<string, Object>
  ): Promise<boolean> {
    try {
      const want: Want = {
        deviceId: '',
        bundleName: bundleName,
        abilityName: abilityName,
        parameters: parameters || {}
      }

      await context.startAbility(want)
      return true
    } catch (error) {
      const err = error as BusinessError
      console.error('启动Ability失败:', err.message)
      return false
    }
  }

  /**
   * 拨打电话
   */
  static async makeCall(
    context: common.UIAbilityContext,
    phoneNumber: string
  ): Promise<boolean> {
    try {
      const want: Want = {
        action: 'ohos.want.action.dial',
        uri: `tel:${phoneNumber}`
      }

      await context.startAbility(want)
      return true
    } catch (error) {
      const err = error as BusinessError
      console.error('拨打电话失败:', err.message)
      return false
    }
  }

  /**
   * 打开浏览器
   */
  static async openUrl(
    context: common.UIAbilityContext,
    url: string
  ): Promise<boolean> {
    try {
      const want: Want = {
        action: 'ohos.want.action.viewData',
        uri: url
      }

      await context.startAbility(want)
      return true
    } catch (error) {
      const err = error as BusinessError
      console.error('打开URL失败:', err.message)
      return false
    }
  }

  /**
   * 分享文本
   */
  static async shareText(
    context: common.UIAbilityContext,
    text: string
  ): Promise<boolean> {
    try {
      const want: Want = {
        action: 'ohos.want.action.sendData',
        type: 'text/plain',
        parameters: {
          'ability.want.params.CONTENT': text
        }
      }

      await context.startAbility(want)
      return true
    } catch (error) {
      const err = error as BusinessError
      console.error('分享失败:', err.message)
      return false
    }
  }

  /**
   * 查看文件
   */
  static async viewFile(
    context: common.UIAbilityContext,
    filePath: string,
    mimeType: string
  ): Promise<boolean> {
    try {
      const want: Want = {
        action: 'ohos.want.action.viewData',
        uri: filePath,
        type: mimeType
      }

      await context.startAbility(want)
      return true
    } catch (error) {
      const err = error as BusinessError
      console.error('查看文件失败:', err.message)
      return false
    }
  }

  /**
   * 发送邮件
   */
  static async sendEmail(
    context: common.UIAbilityContext,
    to: string,
    subject: string,
    body: string
  ): Promise<boolean> {
    try {
      const want: Want = {
        action: 'ohos.want.action.sendto',
        uri: `mailto:${to}`,
        parameters: {
          subject: subject,
          body: body
        }
      }

      await context.startAbility(want)
      return true
    } catch (error) {
      const err = error as BusinessError
      console.error('发送邮件失败:', err.message)
      return false
    }
  }

  /**
   * 打开地图并显示位置
   */
  static async showLocation(
    context: common.UIAbilityContext,
    latitude: number,
    longitude: number,
    label?: string
  ): Promise<boolean> {
    try {
      const want: Want = {
        action: 'ohos.want.action.viewData',
        uri: `geo:${latitude},${longitude}?q=${latitude},${longitude}${label ? `(${label})` : ''}`,
        type: 'application/geo'
      }

      await context.startAbility(want)
      return true
    } catch (error) {
      const err = error as BusinessError
      console.error('打开地图失败:', err.message)
      return false
    }
  }
}

// 使用示例
@Entry
@Component
struct WantUtilDemo {
  private context = getContext(this) as common.UIAbilityContext

  build() {
    Column({ space: 16 }) {
      Button('拨打电话')
        .width('100%')
        .onClick(async () => {
          await WantUtil.makeCall(this.context, '10086')
        })

      Button('打开网页')
        .width('100%')
        .onClick(async () => {
          await WantUtil.openUrl(this.context, 'https://www.harmonyos.com')
        })

      Button('分享文本')
        .width('100%')
        .onClick(async () => {
          await WantUtil.shareText(this.context, 'HarmonyOS开发真棒!')
        })

      Button('显示位置')
        .width('100%')
        .onClick(async () => {
          await WantUtil.showLocation(
            this.context,
            39.9042,
            116.4074,
            '天安门'
          )
        })
    }
    .padding(16)
  }
}

关键要点

  1. Want对象: 应用间通信的载体,包含目标信息和传递数据
  2. 显式Want: 指定bundleName和abilityName,用于启动特定应用
  3. 隐式Want: 指定action和uri,系统自动匹配合适的应用
  4. 参数传递: 通过parameters字段传递数据
  5. 返回结果: 使用startAbilityForResult获取返回数据

最佳实践

  1. 数据序列化: 复杂对象转换为可序列化格式
  2. 异常处理: 捕获启动失败、目标不存在等异常
  3. 权限检查: 某些操作需要用户授权
  4. 参数验证: 接收方验证参数有效性
  5. 用户体验: 提供操作反馈和错误提示

更多关于HarmonyOS 鸿蒙Next中如何实现应用间通信?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙Next应用间通信主要采用分布式软总线技术。支持以下方式:

  1. RPC调用:通过IDL接口定义跨进程服务调用
  2. DataShare:提供数据共享能力,支持跨应用数据访问
  3. Want:用于应用组件间通信,可携带参数启动其他应用组件
  4. EventHub:基于发布订阅模式的事件通信机制

这些机制均基于鸿蒙分布式能力实现,无需依赖传统IPC方式。

在HarmonyOS Next中,应用间通信(Inter-Application Communication, IAC)主要通过 Want 机制实现。这是一种统一的对象,用于在组件和应用之间传递信息,是启动组件(Ability)和进行应用间交互的核心载体。

1. 使用Want实现应用间通信

Want的基本构成:

  • deviceId: 目标设备ID,用于跨设备通信。
  • bundleName: 目标应用包名。
  • abilityName: 要启动的目标Ability名称。
  • uri: 可选,统一资源标识符,常用于指定具体数据。
  • type: 可选,显式指定要处理的数据类型(MIME类型)。
  • action: 操作类型,如ACTION_VIEWACTION_SEND等。
  • entities: 实体类别,用于进一步筛选能处理该Want的组件。
  • parameters: 键值对形式的数据,用于传递额外参数。

主要应用场景:

a. 传递数据与启动Ability 通过隐式Want启动其他应用的Ability,并传递数据。

// 发送方
let want = {
    deviceId: '', // 留空表示本设备
    bundleName: 'com.example.targetapp',
    abilityName: 'EntryAbility',
    parameters: {
        'key': 'value',
        'data': 'Hello from App A'
    }
};
context.startAbility(want).then(() => {
    console.log('启动成功');
}).catch((err) => {
    console.error('启动失败: ' + err);
});

b. 调用其他应用功能 通过actionentities声明功能,实现功能调用。

// 调用分享功能
let want = {
    action: 'ohos.want.action.sendData',
    entities: ['entity.system.share'],
    parameters: {
        'url': 'https://example.com'
    }
};
context.startAbility(want);

c. 跨应用数据共享 通过uri参数结合Data Ability实现数据共享。

// 访问其他应用提供的Data Ability
let want = {
    bundleName: 'com.example.dataapp',
    abilityName: 'DataAbility',
    uri: 'dataability://com.example.dataapp.DataAbility/users'
};
let cursor = await dataAbility.query(want, columns, predicates);

2. HarmonyOS应用间通信方式总结

除了Want,还有以下方式辅助实现通信:

  • 公共事件(Common Event):订阅/发布系统,用于广播通知,适合一对多、松散耦合的场景。
  • 后台代理提醒(Reminder Agent):通过系统提醒服务进行间接通信。
  • 剪贴板(Pasteboard):用于简单的、用户主动触发的数据传递。

关键点:

  • 权限控制:访问其他应用数据或能力需在module.json5中声明所需权限,部分权限需用户动态授权。
  • 隐私保护:Want传递的数据需注意敏感信息保护,避免在parameters中传递大量或敏感数据。
  • 匹配规则:系统根据Want的actionentitiesuritype等属性匹配最合适的Ability。

最佳实践建议:

  1. 明确通信目的,选择最合适的机制(Want启动、公共事件等)。
  2. 使用隐式Want时,清晰定义actionentities,避免冲突。
  3. 跨设备通信需正确设置deviceId并确保设备组网。
  4. 传递大量数据建议使用uri指向Data Ability,而非直接放入parameters

Want机制是HarmonyOS应用间通信的基石,通过灵活组合其属性,可以安全、高效地实现数据传递、功能调用与协同。

回到顶部