> ## Documentation Index
> Fetch the complete documentation index at: https://wukong.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Message Management

> WuKongIM Android SDK message management functionality, including message sending/receiving, history messages and message monitoring

The message manager is responsible for CRUD operations on messages, new message listening, refresh message listening, message storage, send message receipt listening, monitoring sync of specific chat data, etc.

## Sending Messages

### Basic Send Method

<CodeGroup>
  ```java Java theme={null}
  /**
   * Send message
   * @param textContent Message content
   * @param channelID Target channel ID
   * @param channelType Target channel type (personal channel, group channel, customer service channel, etc.)
   */
  WKIM.getInstance().getMsgManager().sendMessage(textContent, channelID, channelType);
  ```

  ```kotlin Kotlin theme={null}
  WKIM.getInstance().msgManager.send(textContent, channel)
  ```
</CodeGroup>

<Note>
  Built-in channel types in SDK can be viewed through `WKChannelType`
</Note>

### Text Messages

<CodeGroup>
  ```java Java theme={null}
  // Define text message
  WKTextContent textContent = new WKTextContent("Hello, WuKong");
  // Send message
  WKIM.getInstance().getMsgManager().send(textContent, channel);
  ```

  ```kotlin Kotlin theme={null}
  // Define text message
  val textContent = WKTextContent("Hello, WuKong")
  // Send message
  WKIM.getInstance().msgManager.send(textContent, channel)
  ```
</CodeGroup>

### Image Messages

<CodeGroup>
  ```java Java theme={null}
  // Define image message
  WKImageContent imageContent = new WKImageContent(localPath);
  // Send message
  WKIM.getInstance().getMsgManager().send(imageContent, channel);
  ```

  ```kotlin Kotlin theme={null}
  // Define image message
  val imageContent = WKImageContent(localPath)
  // Send message
  WKIM.getInstance().msgManager.send(imageContent, channel)
  ```
</CodeGroup>

<Note>
  When building image message content, there's no need to pass image width and height. The SDK will automatically get the image dimensions.
</Note>

### Complete Send Example

```java theme={null}
public class ChatActivity extends AppCompatActivity {
    
    private String channelID;
    private byte channelType;
    
    // Send text message
    private void sendTextMessage(String content) {
        WKTextContent textContent = new WKTextContent(content);
        WKIM.getInstance().getMsgManager().send(textContent, channelID, channelType);
    }
    
    // Send image message
    private void sendImageMessage(String imagePath) {
        WKImageContent imageContent = new WKImageContent(imagePath);
        WKIM.getInstance().getMsgManager().send(imageContent, channelID, channelType);
    }
    
    // Send voice message
    private void sendVoiceMessage(String voicePath, int duration) {
        WKVoiceContent voiceContent = new WKVoiceContent(voicePath, duration);
        WKIM.getInstance().getMsgManager().send(voiceContent, channelID, channelType);
    }
    
    // Send location message
    private void sendLocationMessage(double latitude, double longitude, String address) {
        WKLocationContent locationContent = new WKLocationContent(latitude, longitude, address);
        WKIM.getInstance().getMsgManager().send(locationContent, channelID, channelType);
    }
}
```

### Custom Messages

See custom messages: [Custom Messages](/en/sdk/wukongim/android/advance#custom-messages)

## Message Storage Listening

When sending messages, the SDK will trigger a storage callback after saving the message to the local database. At this point, the message has not been sent yet, and you can display the message in the UI in this listener.

<CodeGroup>
  ```java Java theme={null}
  WKIM.getInstance().getMsgManager().addOnSendMsgCallback("key", new ISendMsgCallBackListener() {
      @Override
      public void onInsertMsg(WKMsg wkMsg) {
          // You can display the message `wkMsg` saved in the database on the UI here
          runOnUiThread(() -> {
              addMessageToUI(wkMsg);
          });
      }
  });
  ```

  ```kotlin Kotlin theme={null}
  WKIM.getInstance().msgManager.addOnSendMsgCallback("key") { wkMsg ->
      // Display message wkMsg on UI
      runOnUiThread {
          addMessageToUI(wkMsg)
      }
  }
  ```
</CodeGroup>

<Note>
  For explanation about whether to pass a unique key for events, see [Event Listening](/en/sdk/wukongim/android#explanation)
</Note>

## New Message Listening

<CodeGroup>
  ```java Java theme={null}
  // Add listener
  WKIM.getInstance().getMsgManager().addOnNewMsgListener("key", new INewMsgListener() {
      @Override
      public void newMsg(List<WKMsg> list) {
          // list: received messages
          runOnUiThread(() -> {
              handleNewMessages(list);
          });
      }
  });

  // Remove listener when exiting page
  WKIM.getInstance().getMsgManager().removeNewMsgListener("key");
  ```

  ```kotlin Kotlin theme={null}
  // Add listener
  WKIM.getInstance().msgManager.addOnNewMsgListener("key") { list ->
      // list: received messages
      runOnUiThread {
          handleNewMessages(list)
      }
  }

  // Remove listener when exiting page
  WKIM.getInstance().msgManager.removeNewMsgListener("key")
  ```
</CodeGroup>

<Note>
  If you receive new messages in a chat page, you need to determine whether the message belongs to the current conversation by checking the `channelID` and `channelType` of the message object `WKMsg`
</Note>

### New Message Handling Example

```java theme={null}
public class ChatActivity extends AppCompatActivity {
    
    private List<WKMsg> messageList = new ArrayList<>();
    private MessageAdapter messageAdapter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // Add new message listener
        WKIM.getInstance().getMsgManager().addOnNewMsgListener("ChatActivity", new INewMsgListener() {
            @Override
            public void newMsg(List<WKMsg> list) {
                handleNewMessages(list);
            }
        });
    }
    
    private void handleNewMessages(List<WKMsg> newMessages) {
        for (WKMsg msg : newMessages) {
            // Check if message belongs to current conversation
            if (msg.channelID.equals(this.channelID) && msg.channelType == this.channelType) {
                runOnUiThread(() -> {
                    messageList.add(msg);
                    messageAdapter.notifyItemInserted(messageList.size() - 1);
                    
                    // Scroll to latest message
                    recyclerView.scrollToPosition(messageList.size() - 1);
                    
                    // Mark message as read
                    markMessageAsRead(msg);
                });
            }
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Remove listener
        WKIM.getInstance().getMsgManager().removeNewMsgListener("ChatActivity");
    }
}
```

## Message Refresh Listening

When the SDK updates messages, such as: message send status, someone likes a message, message read receipt, message recall, message editing, etc., the SDK will callback the following event. The UI can determine which specific message has changed through the `clientMsgNO` of the message object `WKMsg`.

<CodeGroup>
  ```java Java theme={null}
  // Add refresh listener
  WKIM.getInstance().getMsgManager().addOnRefreshMsgListener("key", new IRefreshMsg() {
      @Override
      public void onRefresh(WKMsg wkMsg, boolean isEnd) {
          // wkMsg: refreshed message object
          // isEnd: to avoid frequent UI refreshes causing lag, refresh UI only when isEnd is true
          if (isEnd) {
              runOnUiThread(() -> {
                  refreshMessageInUI(wkMsg);
              });
          }
      }
  });

  // Remove refresh listener when exiting page
  WKIM.getInstance().getMsgManager().removeRefreshMsgListener("key");
  ```

  ```kotlin Kotlin theme={null}
  // Add refresh listener
  WKIM.getInstance().msgManager.addOnRefreshMsgListener("key") { wkMsg, isEnd ->
      // wkMsg: refreshed message object
      // isEnd: to avoid frequent UI refreshes causing lag, refresh UI only when isEnd is true
      if (isEnd) {
          runOnUiThread {
              refreshMessageInUI(wkMsg)
          }
      }
  }

  // Remove refresh listener when exiting page
  WKIM.getInstance().msgManager.removeRefreshMsgListener("key")
  ```
</CodeGroup>

### Message Refresh Handling Example

```java theme={null}
private void refreshMessageInUI(WKMsg updatedMsg) {
    // Find corresponding message by clientMsgNO and update
    for (int i = 0; i < messageList.size(); i++) {
        WKMsg msg = messageList.get(i);
        if (msg.clientMsgNO.equals(updatedMsg.clientMsgNO)) {
            messageList.set(i, updatedMsg);
            messageAdapter.notifyItemChanged(i);
            break;
        }
    }
}
```

## Message Send Status Code (ReasonCode)

When a message is sent, you can obtain the `WKMsg` object by listening for message refresh events. The `status` (send status) and `reasonCode` in `WKMsg` indicate the result of the message delivery.

| Value | Name                         | Description                                |
| ----- | ---------------------------- | ------------------------------------------ |
| 0     | ReasonUnknown                | Unknown error                              |
| 1     | ReasonSuccess                | Success                                    |
| 2     | ReasonAuthFail               | Authentication failed                      |
| 3     | ReasonSubscriberNotExist     | Subscriber does not exist in the channel   |
| 4     | ReasonInBlacklist            | In blacklist                               |
| 5     | ReasonChannelNotExist        | Channel does not exist                     |
| 6     | ReasonUserNotOnNode          | User is not on node                        |
| 7     | ReasonSenderOffline          | Sender is offline, message delivery failed |
| 8     | ReasonMsgKeyError            | Message key error, invalid message         |
| 9     | ReasonPayloadDecodeError     | Payload decoding failed                    |
| 10    | ReasonForwardSendPacketError | Forwarding send packet failed              |
| 11    | ReasonNotAllowSend           | Not allowed to send message                |
| 12    | ReasonConnectKick            | Connection kicked                          |
| 13    | ReasonNotInWhitelist         | Not in whitelist                           |
| 14    | ReasonQueryTokenError        | Query user token error                     |
| 15    | ReasonSystemError            | System error                               |
| 16    | ReasonChannelIDError         | Wrong channel ID                           |
| 17    | ReasonNodeMatchError         | Node matching error                        |
| 18    | ReasonNodeNotMatch           | Node not matched                           |
| 19    | ReasonBan                    | Channel is banned                          |
| 20    | ReasonNotSupportHeader       | Unsupported header                         |
| 21    | ReasonClientKeyIsEmpty       | clientKey is empty                         |
| 22    | ReasonRateLimit              | Rate limit exceeded                        |
| 23    | ReasonNotSupportChannelType  | Unsupported channel type                   |
| 24    | ReasonDisband                | Channel disbanded                          |
| 25    | ReasonSendBan                | Sending is banned                          |

## View History Messages

<CodeGroup>
  ```java Java theme={null}
  /**
   * Query or sync messages for a channel
   *
   * @param channelId                Channel ID
   * @param channelType              Channel type
   * @param oldestOrderSeq           Last message's large orderSeq, pass 0 for first entry into chat
   * @param contain                  Whether to include the oldestOrderSeq message
   * @param dropDown                 Whether it's a dropdown
   * @param aroundMsgOrderSeq        Query messages around this message, e.g. aroundMsgOrderSeq=20 returns [16,17,19,20,21,22,23,24,25]
   * @param limit                    Number to get each time
   * @param iGetOrSyncHistoryMsgBack Request callback
   */
  WKIM.getInstance().getMsgManager().getOrSyncHistoryMessages(
      channelId, 
      channelType, 
      oldestOrderSeq, 
      contain, 
      dropDown, 
      limit, 
      aroundMsgOrderSeq, 
      new IGetOrSyncHistoryMsgBack() {
          @Override
          public void onSyncing() {
              // Syncing - show loading as needed
          }

          @Override
          public void onResult(List<WKMsg> list) {
              // Display messages
          }
      }
  );
  ```

  ```kotlin Kotlin theme={null}
  WKIM.getInstance().msgManager.getOrSyncHistoryMessages(
      channelId,
      channelType,
      oldestOrderSeq,
      contain,
      dropDown,
      limit,
      aroundMsgOrderSeq,
      object : IGetOrSyncHistoryMsgBack {
          override fun onSyncing() {
              // Syncing
          }
          
          override fun onResult(list: MutableList<WKMsg>?) {
              // list: retrieved messages, display in UI
          }
      }
  )
  ```
</CodeGroup>

<Note>
  Getting history messages is not a synchronous method, as there may be non-continuous data that needs to be synced from the server
</Note>

## Next Steps

<CardGroup cols={2}>
  <Card title="Channel Management" icon="hash" href="/en/sdk/wukongim/android/channel">
    Learn how to manage channels and groups
  </Card>

  <Card title="Conversation Management" icon="users" href="/en/sdk/wukongim/android/conversation">
    Handle conversation lists and unread messages
  </Card>

  <Card title="Channel Member Management" icon="user-group" href="/en/sdk/wukongim/android/channel-member">
    Manage channel member information
  </Card>

  <Card title="Data Source Configuration" icon="database" href="/en/sdk/wukongim/android/datasource">
    Configure data sources and sync logic
  </Card>
</CardGroup>
