# 实现收发消息 (/zh-CN/realtime-media/rtm/get-started/quick-start/harmonyos)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

本文将指导你如何利用 RTM HarmonyOS SDK 构建简单的应用程序，内容涵盖了集成和开发的基础知识：开通声网账号、获取 SDK、发送消息、接收消息等。
## 准备工作 [#准备工作-3]

开始构建项目前，你需要完成[开通 RTM 服务](/zh-CN/realtime-media/rtm/build/rtm-initialization/enable-service)，并检查你的开发环境是否满足[平台支持](/zh-CN/realtime-media/rtm/reference/capabilities-and-compatibility/platform-support)中的最低版本要求。

## 构建项目 [#构建项目-3]

### 1. 获取 SDK [#1-获取-sdk]

[点击此处](https://download.shengwang.cn/rtm2/release/RTM_ArkTS_SDK_for_HarmonyOS_v2.3.0.zip)下载最新版本的 HarmonyOS SDK。

下载完成后，解压 SDK 包，你将获得一个 `.har` 文件，这是 HarmonyOS 的静态共享包格式。

:::info[信息]
如果你同时集成了 2.2.0 及以上版本的 RTM SDK 和 4.3.0 及以上版本的 RTC SDK，请参考 [FAQ](/zh-CN/reference/faq/integration/rtm2_rtc_integration_issue) 处理集成问题。
:::

### 2. 项目配置 [#2-项目配置]

根据以下步骤配置你的项目：

1. 在 DevEco Studio 中，创建一个新的 HarmonyOS 应用项目。

2. 将下载的 `.har` 文件导入到你的项目中：
   - 在项目根目录下创建 `libs` 文件夹（如果不存在）
   - 将 `.har` 文件复制到 `libs` 文件夹中
   - 在模块的 `oh-package.json5` 文件中添加依赖：

   ```json
   {
     "dependencies": {
        "@shengwang/rtm-full": "file:./libs/AgoraRtmSDK.har"
     }
   }
   ```

3. 在模块的 `module.json5` 文件中添加网络权限：

   ```json
   {
     "module": {
       "requestPermissions": [
          {
            "name": "ohos.permission.INTERNET"
          },
          {
            "name": "ohos.permission.GET_NETWORK_INFO"
          }
       ]
     }
   }
   ```

4. 同步项目依赖，确保 SDK 正确导入。

### 3. 创建并初始化 RTM [#3-创建并初始化-rtm]

调用 RTM SDK 的任何 API 之前，需要先创建一个 `RtmClient` 对象实例。在你的项目中创建一个 ArkTS 文件（例如 `RtmManager.ets`），添加以下代码作为程序模版。你会发现此程序并不完整，不用紧张，我们在后续步骤中将一步步指导你补充完善代码。

:::info[信息]
你需要在初始化时填入已开通 RTM 服务的项目的 App ID 和 Token。在测试阶段，为快速验证功能，我们建议你在创建声网项目时将鉴权模式设置为调试模式，然后在示例中填入项目的 App ID 即可。
:::

```typescript
import { RtmClient, RtmConfig, RtmChannelType, SubscribeOptions, PublishOptions } from '@shengwang/rtm-full';
import type { LinkStateEvent, MessageEvent, PresenceEvent } from '@shengwang/rtm-full';
import { RtmError } from '@shengwang/rtm-full';

// 填入你的 App ID
const APP_ID = '<Your App ID>';
// 填入你的 Token（测试阶段可以为空字符串）
const TOKEN = '';

export class RtmManager {
  private rtmClient: RtmClient | null = null;
  private userId: string = '';

  /**
   * 初始化 RTM 客户端
   */
  async initialize(userId: string): Promise<void> {
    try {
      this.userId = userId;
      
      // 创建 RTM 配置
      const config: RtmConfig = {
        appId: APP_ID,
        userId: userId
      };

      // 创建 RTM 客户端实例
      this.rtmClient = new RtmClient(config);
      
      // 添加事件监听（后续步骤中补充）
      
      console.log('RTM client initialized successfully');
    } catch (error) {
      console.error('Failed to initialize RTM client:', error);
      if (error instanceof RtmError) {
        throw new Error(`初始化失败: ${error.reason} (code: ${error.errorCode})`);
      } else {
        throw new Error('RTM client initialization failed');
      }
    }
  }

  /**
   * 登录 RTM 服务
   */
  async login(): Promise<void> {
    // 后续步骤中补充
  }

  /**
   * 订阅频道
   */
  async subscribe(channelName: string): Promise<void> {
    // 后续步骤中补充
  }

  /**
   * 取消订阅频道
   */
  async unsubscribe(channelName: string): Promise<void> {
    // 后续步骤中补充
  }

  /**
   * 发送消息
   */
  async publish(channelName: string, message: string): Promise<void> {
    // 后续步骤中补充
  }

  /**
   * 登出 RTM 服务
   */
  async logout(): Promise<void> {
    // 后续步骤中补充
  }

  /**
   * 释放资源
   */
  release(): void {
    if (this.rtmClient) {
      this.rtmClient.release();
      this.rtmClient = null;
    }
  }
}
```

### 4. 添加事件监听 [#4-添加事件监听]

事件监听程序帮助你实现频道中消息、事件到达后的处理逻辑。在 `initialize` 方法中添加事件监听器，以显示收到的消息或事件通知：

```typescript showLineNumbers
// 在 initialize 方法中，创建 RTM 客户端后添加以下代码
// 添加事件监听（替换之前的注释）

// 监听连接状态变化
this.rtmClient.addEventListener('linkState', (event) => {
  console.log('Link state changed:', event);
  console.log(`State: ${event.previousState} -> ${event.currentState}`);
  console.log(`Reason: ${event.reason}`);
});

// 监听接收到的消息
this.rtmClient.addEventListener('message', (event) => {
  console.log('Received message:', event);
  console.log(`From: ${event.publisher}`);
  console.log(`Channel: ${event.channelName}`);
  console.log(`Message: ${event.message}`);
});

// 监听 Presence 事件（用户加入/离开）
this.rtmClient.addEventListener('presence', (event) => {
  console.log('Presence event:', event);
  console.log(`Event type: ${event.eventType}`);
  console.log(`User: ${event.publisher}`);
});
```

### 5. 登录服务 [#5-登录服务]

你需要执行登录操作才能建立与 RTM 服务器的连接，然后才能调用 SDK 的其他 API。在 `login` 方法中添加以下代码：

```typescript showLineNumbers
// 在 login 方法中添加以下代码
async login(): Promise<void> {
  if (!this.rtmClient) {
    throw new Error('RTM client not initialized');
  }

  try {
    const response = await this.rtmClient.login(TOKEN);
    console.log('Login successful:', response);
  } catch (error) {
    console.error('Login failed:', error);
    if (error instanceof RtmError) {
      throw new Error(`登录失败: ${error.reason} (code: ${error.errorCode})`);
    } else {
      throw new Error('Login failed');
    }
  }
}
```

:::info[信息]
你需要在登录时传入 Token。在测试阶段，为快速验证功能，我们建议你在创建声网项目时将鉴权模式设置为调试模式，然后在登录时传入空字符串即可。
:::

### 6. 收发消息 [#6-收发消息]

调用 `publish` 方法向 Message Channel 发送消息后，RTM 会把该消息分发给该频道的所有订阅者。以下代码演示如何发送字符串类型的消息：

:::info[信息]
你需要先对消息负载进行字符串序列化，才能调用 `publish` 方法发送消息。
:::

```typescript showLineNumbers
// 在 publish 方法中添加以下代码
async publish(channelName: string, message: string): Promise<void> {
  if (!this.rtmClient) {
    throw new Error('RTM client not initialized');
  }

  try {
    const options: PublishOptions = {
      channelType: RtmChannelType.MESSAGE,
      customType: ''
    };
    
    const response = await this.rtmClient.publish(channelName, message, options);
    console.log('Message published successfully:', response);
  } catch (error) {
    console.error('Failed to publish message:', error);
    if (error instanceof RtmError) {
      throw new Error(`发送消息失败: ${error.reason} (code: ${error.errorCode})`);
    } else {
      throw new Error('Publish failed');
    }
  }
}
```

调用 `subscribe` 方法订阅此频道以接收此频道中的消息：

```typescript showLineNumbers
// 在 subscribe 方法中添加以下代码
async subscribe(channelName: string): Promise<void> {
  if (!this.rtmClient) {
    throw new Error('RTM client not initialized');
  }

  try {
    const options: SubscribeOptions = {
      withMessage: true,
      withMetadata: false,
      withPresence: true,
      withLock: false
    };
    
    const response = await this.rtmClient.subscribe(channelName, options);
    console.log('Subscribed to channel successfully:', response);
  } catch (error) {
    console.error('Failed to subscribe to channel:', error);
    if (error instanceof RtmError) {
      throw new Error(`订阅失败: ${error.reason} (code: ${error.errorCode})`);
    } else {
      throw new Error('Subscribe failed');
    }
  }
}
```

如果你不再需要在此频道收发消息，你可以调用 `unsubscribe` 方法取消订阅此频道：

```typescript showLineNumbers
// 在 unsubscribe 方法中添加以下代码
async unsubscribe(channelName: string): Promise<void> {
  if (!this.rtmClient) {
    throw new Error('RTM client not initialized');
  }

  try {
    const response = await this.rtmClient.unsubscribe(channelName);
    console.log('Unsubscribed from channel successfully:', response);
  } catch (error) {
    console.error('Failed to unsubscribe from channel:', error);
    if (error instanceof RtmError) {
      throw new Error(`取消订阅失败: ${error.reason} (code: ${error.errorCode})`);
    } else {
      throw new Error('Unsubscribe failed');
    }
  }
}
```

如需了解更多收发消息的信息，查看[消息](/zh-CN/api-reference/rtm/harmonyos/message)。

### 7. 登出服务 [#7-登出服务]

当不再需要使用 RTM 服务时，你可以调用 `logout` 方法登出 RTM 系统：

```typescript showLineNumbers
// 在 logout 方法中添加以下代码
async logout(): Promise<void> {
  if (!this.rtmClient) {
    throw new Error('RTM client not initialized');
  }

  try {
    const response = await this.rtmClient.logout();
    console.log('Logout successful:', response);
  } catch (error) {
    console.error('Logout failed:', error);
    if (error instanceof RtmError) {
      throw new Error(`登出失败: ${error.reason} (code: ${error.errorCode})`);
    } else {
      throw new Error('Logout failed');
    }
  }
}
```

:::info[信息]
本操作会影响你账单中的 PCU 计费项。
:::

### 8. 组合到一起 [#8-组合到一起]

经过上述步骤，你的完整代码应该如下所示：

```typescript
import { RtmClient, RtmConfig, RtmChannelType, SubscribeOptions, PublishOptions } from '@shengwang/rtm-full';
import type { LinkStateEvent, MessageEvent, PresenceEvent } from '@shengwang/rtm-full';
import { RtmError } from '@shengwang/rtm-full';

// 填入你的 App ID
const APP_ID = '<Your App ID>';
// 填入你的 Token（测试阶段可以为空字符串）
const TOKEN = '';

export class RtmManager {
  private rtmClient: RtmClient | null = null;
  private userId: string = '';

  /**
   * 初始化 RTM 客户端
   */
  async initialize(userId: string): Promise<void> {
    try {
      this.userId = userId;
      
      // 创建 RTM 配置
      const config: RtmConfig = {
        appId: APP_ID,
        userId: userId
      };

      // 创建 RTM 客户端实例
      this.rtmClient = new RtmClient(config);
      
      // 监听连接状态变化
      this.rtmClient.addEventListener('linkState', (event: object) => {
        const linkEvent = event as LinkStateEvent;
        console.log('Link state changed:', linkEvent);
        console.log(`State: ${linkEvent.previousState} -> ${linkEvent.currentState}`);
        console.log(`Reason: ${linkEvent.reason}`);
      });

      // 监听接收到的消息
      this.rtmClient.addEventListener('message', (event: object) => {
        const msgEvent = event as MessageEvent;
        console.log('Received message:', msgEvent);
        console.log(`From: ${msgEvent.publisher}`);
        console.log(`Channel: ${msgEvent.channelName}`);
        console.log(`Message: ${msgEvent.message}`);
      });

      // 监听 Presence 事件（用户加入/离开）
      this.rtmClient.addEventListener('presence', (event: object) => {
        const presenceEvent = event as PresenceEvent;
        console.log('Presence event:', presenceEvent);
        console.log(`Event type: ${presenceEvent.eventType}`);
        console.log(`User: ${presenceEvent.publisher}`);
      });
      
      console.log('RTM client initialized successfully');
    } catch (error) {
      console.error('Failed to initialize RTM client:', error);
      if (error instanceof RtmError) {
        throw new Error(`初始化失败: ${error.reason} (code: ${error.errorCode})`);
      } else {
        throw new Error('RTM client initialization failed');
      }
    }
  }

  /**
   * 登录 RTM 服务
   */
  async login(): Promise<void> {
    if (!this.rtmClient) {
      throw new Error('RTM client not initialized');
    }

    try {
      const response = await this.rtmClient.login(TOKEN);
      console.log('Login successful:', response);
    } catch (error) {
      console.error('Login failed:', error);
      if (error instanceof RtmError) {
        throw new Error(`登录失败: ${error.reason} (code: ${error.errorCode})`);
      } else {
        throw new Error('Login failed');
      }
    }
  }

  /**
   * 订阅频道
   */
  async subscribe(channelName: string): Promise<void> {
    if (!this.rtmClient) {
      throw new Error('RTM client not initialized');
    }

    try {
      const options: SubscribeOptions = {
        withMessage: true,
        withMetadata: false,
        withPresence: true,
        withLock: false
      };
      
      const response = await this.rtmClient.subscribe(channelName, options);
      console.log('Subscribed to channel successfully:', response);
    } catch (error) {
      console.error('Failed to subscribe to channel:', error);
      if (error instanceof RtmError) {
        throw new Error(`订阅失败: ${error.reason} (code: ${error.errorCode})`);
      } else {
        throw new Error('Subscribe failed');
      }
    }
  }

  /**
   * 取消订阅频道
   */
  async unsubscribe(channelName: string): Promise<void> {
    if (!this.rtmClient) {
      throw new Error('RTM client not initialized');
    }

    try {
      const response = await this.rtmClient.unsubscribe(channelName);
      console.log('Unsubscribed from channel successfully:', response);
    } catch (error) {
      console.error('Failed to unsubscribe from channel:', error);
      if (error instanceof RtmError) {
        throw new Error(`取消订阅失败: ${error.reason} (code: ${error.errorCode})`);
      } else {
        throw new Error('Unsubscribe failed');
      }
    }
  }

  /**
   * 发送消息
   */
  async publish(channelName: string, message: string): Promise<void> {
    if (!this.rtmClient) {
      throw new Error('RTM client not initialized');
    }

    try {
      const options: PublishOptions = {
        channelType: RtmChannelType.MESSAGE,
        customType: ''
      };
      
      const response = await this.rtmClient.publish(channelName, message, options);
      console.log('Message published successfully:', response);
    } catch (error) {
      console.error('Failed to publish message:', error);
      if (error instanceof RtmError) {
        throw new Error(`发送消息失败: ${error.reason} (code: ${error.errorCode})`);
      } else {
        throw new Error('Publish failed');
      }
    }
  }

  /**
   * 登出 RTM 服务
   */
  async logout(): Promise<void> {
    if (!this.rtmClient) {
      throw new Error('RTM client not initialized');
    }

    try {
      const response = await this.rtmClient.logout();
      console.log('Logout successful:', response);
    } catch (error) {
      console.error('Logout failed:', error);
      if (error instanceof RtmError) {
        throw new Error(`登出失败: ${error.reason} (code: ${error.errorCode})`);
      } else {
        throw new Error('Logout failed');
      }
    }
  }

  /**
   * 释放资源
   */
  release(): void {
    if (this.rtmClient) {
      this.rtmClient.release();
      this.rtmClient = null;
    }
  }
}
```

现在，你可以在你的 HarmonyOS 应用中使用这个 `RtmManager` 类。以下是一个简单的页面示例，展示如何在 Index 页面中使用 RTM 功能：

```typescript
// Index.ets
import { RtmManager } from './RtmManager';
import { RtmChannelType } from '@shengwang/rtm-full';

@Entry
@Component
struct Index {
  @State userId: string = '';
  @State channelName: string = '';
  @State message: string = '';
  @State messageHistory: string = '';
  @State isLoggedIn: boolean = false;
  
  private rtmManager: RtmManager = new RtmManager();

  build() {
    Column() {
      // 标题
      Text('RTM 快速开始')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 20 })

      // 用户 ID 输入
      Row() {
        Text('用户 ID:')
          .width(80)
        TextInput({ placeholder: '请输入用户 ID' })
          .layoutWeight(1)
          .onChange((value: string) => {
            this.userId = value;
          })
      }
      .width('90%')
      .margin({ bottom: 10 })

      // 登录/登出按钮
      Row() {
        Button(this.isLoggedIn ? '登出' : '登录')
          .width('45%')
          .onClick(() => {
            if (this.isLoggedIn) {
              this.handleLogout();
            } else {
              this.handleLogin();
            }
          })
      }
      .width('90%')
      .margin({ bottom: 20 })

      Divider()
        .margin({ bottom: 20 })

      // 频道名输入
      Row() {
        Text('频道名:')
          .width(80)
        TextInput({ placeholder: '请输入频道名' })
          .layoutWeight(1)
          .onChange((value: string) => {
            this.channelName = value;
          })
      }
      .width('90%')
      .margin({ bottom: 10 })

      // 订阅/取消订阅按钮
      Row() {
        Button('订阅')
          .width('45%')
          .onClick(() => {
            this.handleSubscribe();
          })
          .enabled(this.isLoggedIn)

        Button('取消订阅')
          .width('45%')
          .onClick(() => {
            this.handleUnsubscribe();
          })
          .enabled(this.isLoggedIn)
      }
      .width('90%')
      .justifyContent(FlexAlign.SpaceBetween)
      .margin({ bottom: 20 })

      // 消息输入
      Row() {
        Text('消息:')
          .width(80)
        TextInput({ placeholder: '请输入消息内容' })
          .layoutWeight(1)
          .onChange((value: string) => {
            this.message = value;
          })
      }
      .width('90%')
      .margin({ bottom: 10 })

      // 发送消息按钮
      Button('发送消息')
        .width('90%')
        .onClick(() => {
          this.handlePublish();
        })
        .enabled(this.isLoggedIn)
        .margin({ bottom: 20 })

      Divider()
        .margin({ bottom: 10 })

      // 消息历史
      Text('消息历史:')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .alignSelf(ItemAlign.Start)
        .margin({ left: '5%', bottom: 10 })

      Scroll() {
        Text(this.messageHistory)
          .width('100%')
          .padding(10)
      }
      .width('90%')
      .height(200)
      .backgroundColor('#F0F0F0')
      .borderRadius(8)
    }
    .width('100%')
    .height('100%')
    .padding({ top: 20 })
  }

  // 登录处理
  async handleLogin() {
    if (!this.userId) {
      this.addMessage('请输入用户 ID');
      return;
    }

    try {
      await this.rtmManager.initialize(this.userId);
      await this.rtmManager.login();
      this.isLoggedIn = true;
      this.addMessage(`用户 ${this.userId} 登录成功`);
    } catch (error) {
      this.addMessage(`登录失败: ${error}`);
    }
  }

  // 登出处理
  async handleLogout() {
    try {
      await this.rtmManager.logout();
      this.rtmManager.release();
      this.isLoggedIn = false;
      this.addMessage('登出成功');
    } catch (error) {
      this.addMessage(`登出失败: ${error}`);
    }
  }

  // 订阅频道
  async handleSubscribe() {
    if (!this.channelName) {
      this.addMessage('请输入频道名');
      return;
    }

    try {
      await this.rtmManager.subscribe(this.channelName);
      this.addMessage(`订阅频道 ${this.channelName} 成功`);
    } catch (error) {
      this.addMessage(`订阅失败: ${error}`);
    }
  }

  // 取消订阅频道
  async handleUnsubscribe() {
    if (!this.channelName) {
      this.addMessage('请输入频道名');
      return;
    }

    try {
      await this.rtmManager.unsubscribe(this.channelName);
      this.addMessage(`取消订阅频道 ${this.channelName} 成功`);
    } catch (error) {
      this.addMessage(`取消订阅失败: ${error}`);
    }
  }

  // 发送消息
  async handlePublish() {
    if (!this.channelName) {
      this.addMessage('请输入频道名');
      return;
    }

    if (!this.message) {
      this.addMessage('请输入消息内容');
      return;
    }

    try {
      await this.rtmManager.publish(this.channelName, this.message);
      this.addMessage(`发送消息到 ${this.channelName}: ${this.message}`);
    } catch (error) {
      this.addMessage(`发送消息失败: ${error}`);
    }
  }

  // 添加消息到历史记录
  addMessage(msg: string) {
    const timestamp = new Date().toLocaleTimeString();
    this.messageHistory += `[${timestamp}] ${msg}\n`;
  }
}
```

这个示例展示了如何：

1. **创建 RTM 管理器实例**：在组件中创建 `RtmManager` 实例
2. **用户登录**：输入用户 ID，点击登录按钮初始化并登录 RTM
3. **订阅频道**：输入频道名，点击订阅按钮订阅消息频道
4. **发送消息**：输入消息内容，点击发送按钮向频道发送消息
5. **取消订阅**：点击取消订阅按钮退出频道
6. **用户登出**：点击登出按钮断开 RTM 连接

你也可以使用更简单的方式来测试 RTM 功能：

```typescript
// 在你的页面或组件中使用
import { RtmManager } from './RtmManager';

// 创建 RTM 管理器实例
const rtmManager = new RtmManager();

// 初始化并登录
await rtmManager.initialize('user123');
await rtmManager.login();

// 订阅频道
await rtmManager.subscribe('test-channel');

// 发送消息
await rtmManager.publish('test-channel', 'Hello, RTM!');

// 取消订阅
await rtmManager.unsubscribe('test-channel');

// 登出
await rtmManager.logout();

// 释放资源
rtmManager.release();
```

至此，你已经成功集成并正确使用了 RTM 服务。

## 贯穿始终 [#贯穿始终-3]

相比于介绍如何写出代码，声网更愿意帮助你掌握上述程序编写的过程和逻辑。上述程序依次完成了以下操作，让你可以正确地收发消息：

1. 创建并初始化 RTM 对象。
2. 添加 `linkState`、`message` 和 `presence` 事件监听函数。
3. 调用 `login` 登录了 RTM 服务。
4. 调用 `subscribe` 订阅了一个 Message Channel。
5. 调用 `publish` 发送消息。
6. 调用 `unsubscribe` 取消订阅一个 Message Channel。
7. 调用 `logout` 登出 RTM 系统。

## 下一步 [#下一步-3]

现在，你已经学会了如何使用 RTM HarmonyOS SDK 来实现 Message Channel 的收发消息功能。下一步，你可以通过 SDK 的 [API 参考](/zh-CN/api-reference/rtm/harmonyos/configuration)了解更多功能的使用方法。










