Send and receive messages
Updated
Shows how to implement sending and receiving these messages using the Agora Chat SDK.
After logging in to Chat, users can send the following types of messages to a peer user, a chat group, or a chat room:
-
Text messages, including hyperlinks and emojis.
-
Attachment messages, including image, voice, video, and file messages.
-
Location messages.
-
CMD messages.
-
Extended messages.
-
Custom messages.
The Chat message feature is language agnostic. End users can send messages in any language, as long as their devices support input in that language.
In addition to sending messages, users can also forward one or more messages. When forwarding multiple messages, users have the following options:
- Forward messages one-by-one
- Forward combined messages as message history
This page shows how to implement sending, receiving, forwarding multiple messages, and modifying sent messages using the Chat SDK.
Understand the tech
The Chat SDK uses the ChatManager class to send, receive, and withdraw messages.
The process of sending and receiving a message is as follows:
- The message sender creates a text, file, or attachment message using the corresponding
createmethod. - The message sender calls
sendMessageto send the message. - The message recipient calls
addMessageListenerto listens for message events and receive the message in theOnMessageReceivedcallback.
Prerequisites
Before proceeding, ensure that you meet the following requirements:
-
You have integrated the Chat SDK, initialized the SDK, and implemented the functionality of registering accounts and login. For details, see Chat SDK quickstart.
Use Chat SDK 1.2 or higher if you intend to enable users to forward multiple messages, or to modify sent messages.
-
You understand the API call frequency limits as described in Limitations.
Implementation
This section shows how to implement sending and receiving the various types of messages.
Send a text message
Use the ChatMessage class to create a text message, and send the message.
// Call createTextSendMessage to create a text message.
// Set `content` as the content of the text message.
// Set `conversationId` to the user ID of the peer user in one-to-one chat, group ID in group chat, and chat room ID in room chat.
ChatMessage message = ChatMessage.createTextSendMessage(content, conversationId);
// Set `ChatType` as `Chat`, `GroupChat`, or `ChatRoom` for one-to-one chat, group chat, or room chat.
message.setChatType(ChatMessage.GroupChat);
// Send the message.
ChatClient.getInstance().chatManager().sendMessage(message);// Call setMessageStatusCallback to set the callback instance to get the status of messaging sending.
// You can update the status of messages in this callback, for example, adding a tip when the message sending fails.
message.setMessageStatusCallback(new CallBack() {
@Override
public void onSuccess() {
showToast("Message sending succeeds");
}
@Override
public void onError(int code, String error) {
showToast("Message sending fails");
}
});
ChatClient.getInstance().chatManager().sendMessage(message);Send a message to online users only
Agora Chat supports delivering messages only to users that are currently online. An example of using this function is displaying the votes in a group voting: Only users that are online need to see the changes in real time, while the others only need the final result.
All types of messages in one-to-one chats and chat groups support this function. Compared to an ordinary message, a message that is delivered only to online users has the following differences:
- Does not support offline storage: If the recipient is offline when sending a message, the message cannot be received, even after logging in again. For ordinary messages, when the recipient is online, the message reminder is received in real time; when the recipient is offline, the offline push message is sent in real time. When the recipient is online again, the Chat server actively pushes the offline message to the client.
- Support local storage: After a message is successfully sent, it is added to the database.
- Roaming storage is not supported by default: Sent messages are not stored on the Chat message server by default, so users cannot obtain the messages on other devices. If you need to activate roaming storage of online messages, contact support@agora.io.
To deliver messages to online users only, you need to set ChatMessage#deliverOnlineOnly to true when sending the message. The following is an example of sending a text message to only online users:
// Create a text message, `content` is the text content of the message.
// `conversationId` is the message receiver. It is the peer user ID in single chat and the group ID in group chat.
ChatMessage message = ChatMessage.createTextSendMessage(content, conversationId);
// Whether the message is only delivered to online users. (Default) `false`: Delivered regardless of whether the user is online or not; `true`: Only delivered to online users. If the user is offline, the message will not be delivered.
message.deliverOnlineOnly(true);
// Conversation type: Single chat is ChatMessage.ChatType.Chat, group chat is ChatMessage.ChatType.GroupChat, the default is single chat.
message.setChatType(ChatMessage.ChatType.Chat);
// Send a message.
ChatClient.getInstance().chatManager().sendMessage(message);Set message priority
In high-concurrency use-cases, you can set a certain message type or messages from a chat room member as high, normal, or low priority. In this case, low-priority messages are dropped first to reserve resources for the high-priority ones, for example, gifts and announcements, when the server is overloaded. This ensures that the high-priority messages can be dealt with first when loads of messages are being sent in high concurrency or high frequency. Note that this feature can increase the delivery reliability of high-priority messages, but cannot guarantee the deliveries. Even high-priorities messages can be dropped when the server load goes too high.
You can set the priority for all types of messages in the chat room.
// Set `content` as the content of the text message.
// Set `conversationId` to chat room ID in room chat.
ChatMessage message = ChatMessage.createTextSendMessage(content, conversationId);
message.setChatType(ChatMessage.ChatType.ChatRoom);
// Set the message priority. The default value is `PriorityNormal`, indicating the normal priority.
message.setPriority(ChatMessage.ChatRoomMessagePriority.PriorityHigh);
ChatClient.getInstance().chatManager().sendMessage(message);Receive a message
You can use MessageListener to listen for message events. You can add multiple MessageListeners to listen for multiple events. When you no longer listen for an event, ensure that you remove the listener.
When a message arrives, the recipient receives an onMessagesReceived callback. Each callback contains one or more messages. You can traverse the message list, and parse and render these messages in this callback.
MessageListener msgListener = new MessageListener() {
// Traverse the message list, and parse and render the messages.
@Override
public void onMessageReceived(List messages) {
}
};
// Add a message listener
ChatClient.getInstance().chatManager().addMessageListener(msgListener);
// Remove a message listener
ChatClient.getInstance().chatManager().removeMessageListener(msgListener);
ChatClient.getInstance().chatManager().removeMessageListener(msgListener);Recall a message
After a message is sent, you can recall it. The recallMessage method recalls a message that is saved both locally and on the server, whether it is a historical message, offline message or a roaming message on the server, or a message in the memory or local database of the message sender or recipient.
The default time limit for recalling a message is two minutes. You can extend this time frame to up to 7 days in Agora Console. To do so, select a project that enables Agora Chat, then click Configure > Features > Message recall.
The following permission rules apply to message recall:
- In one-to-one chats, only the message sender can recall a message they sent. If the message has expired, the recall fails.
- In chat groups and chat rooms, regular members can recall only the messages they sent, and the recall fails if the message has expired. Since SDK v1.4.0, the chat group owner, chat room owner, and admins can recall messages sent by other members, even after those messages have expired.
- Except CMD messages, you can recall all types of message. 2. If an attachment message, like an image, voice, video, or file message, is recalled, the attachment of the message is also deleted.
try {
ChatClient.getInstance().chatManager().recallMessage(message);
EMLog.d("TAG", "Recalling message succeeds");
} catch (ChatException e) {
e.printStackTrace();
EMLog.e("TAG", "Recalling message fails: "+e.getDescription());
}You can also use onMessageRecalled to listen for the message recall state:
/**
* Occurs when a received message is recalled.
*/
void onMessageRecalled(List messages);Send and receive an attachment message
Voice, image, video, and file messages are essentially attachment messages. This section introduces how to send these types of messages.
Send and receive a voice message
Before sending a voice message, you should implement audio recording on the app level, which provides the URI and duration of the recorded audio file.
Refer to the following code example to create and send a voice message:
// Set voiceUri as the local URI of the audio file, and duration as the length of the file in seconds.
ChatMessage message = ChatMessage.createVoiceSendMessage(voiceUri, duration, conversationId);
// Sets the chat type as one-to-one chat, group chat, or chatroom.
if (chatType == CHATTYPE_GROUP) {
message.setChatType(ChatType.GroupChat);
}
ChatClient.getInstance().chatManager().sendMessage(message);When the recipient receives the message, refer to the following code example to get the audio file:
VoiceMessageBody voiceBody = (VoiceMessageBody) msg.getBody();
// Retrieves the URL of the audio file on the server.
String voiceRemoteUrl = voiceBody.getRemoteUrl();
// Retrieves the URI if the audio file on the local device.
//If 'ChatClient.getInstance().getOptions().setAutodownloadThumbnail' is set as `false` or the attachment has not been downloaded yet,
//the file may not exist in 'voiceLocalUri' and you can manually call 'ChatClient.getInstance().chatManager().downloadAttachment(message)' to download the attachment,
//and call the 'message. SetMessageStatusCallback (the callback)' to see if the download succeeds.
Uri voiceLocalUri = voiceBody.getLocalUri();Send and receive an image message
By default, the SDK compresses the image file before sending it. To send the original file, you can set original as true.
Refer to the following code example to create and send an image message:
// Set imageUri as the URI of the image file on the local device. false means not to send the original image. The SDK compresses image files that exceeds 100K before sending them.
ChatMessage.createImageSendMessage(imageUri, false, conversationId);
// Sets the chat type as one-to-one chat, group chat, or chatroom.
if (chatType == CHATTYPE_GROUP)
message.setChatType(ChatType.GroupChat);
ChatClient.getInstance().chatManager().sendMessage(message);When the recipient receives the message, refer to the following code example to get the thumbnail and attachment file of the image message:
// Retrieves the thumbnail and attachment of the image file.
ImageMessageBody imgBody = (ImageMessageBody) message.getBody();
// Retrieves the image file the server.
String imgRemoteUrl = imgBody.getRemoteUrl();
// Retrieves the image thumbnail from the server.
String thumbnailUrl = imgBody.getThumbnailUrl();
// Retrives the URI of the image file on the local device.
Uri imgLocalUri = imgBody.getLocalUri();
// Retrieves the URI of the image thumbnail on the local device.
Uri thumbnailLocalUri = imgBody.thumbnailLocalUri();If ChatClient.getInstance().getOptions().getAutodownloadThumbnail() is set as true on the recipient's client, the SDK automatically downloads the thumbnail after receiving the message. If not, you need to call ChatClient.getInstance().chatManager().downloadThumbnail(message) to download the thumbnail and get the path from the thumbnailLocalUri member in messageBody.
If the file does not exist in imgLocalUri, you can manually call ChatClient.getInstance().chatManager().downloadAttachment(message) to download the attachment, and call the message. SetMessageStatusCallback (the callback)to see if the download succeeds.
Send and receive a GIF image message
Since SDK v1.4.0, you can send and receive GIF image messages. A GIF image message is a special type of image message. Unlike a regular image message, a GIF image is not compressed when sent. Thumbnail generation is the same as for a regular image message; see Send and receive an image message.
To send a GIF image message, call createGifImageMessage to construct the message body, then call sendMessage. The SDK uploads the image to the Agora Chat server, which automatically generates the thumbnail.
// Set imageUri as the URI of the GIF image file on the local device.
ChatMessage message = ChatMessage.createGifImageMessage(imageUri, conversationId);
// Sets the chat type as one-to-one chat, group chat, or chatroom. The default is one-to-one chat.
if (chatType == CHATTYPE_GROUP)
message.setChatType(ChatType.GroupChat);
ChatClient.getInstance().chatManager().sendMessage(message);As with a regular message, the recipient receives the onMessageReceived callback. After confirming that the message is an image message, read the isGif() property of the message body. If it returns true, the message is a GIF image message.
public void onMessageReceived(List<ChatMessage> messages) {
for (ChatMessage message : messages) {
if (message.getType() == ChatMessage.Type.IMAGE) {
ImageMessageBody body = (ImageMessageBody) message.getBody();
if (body.isGif()) {
// Handle the GIF image message according to your business logic, for example, download and display it.
}
}
}
}Send and receive a video message
Before sending a video message, you should implement video capturing on the app level, which provides the duration of the captured video file.
Refer to the following code example to create and send a video message:
String thumbPath = getThumbPath(videoUri);
ChatMessage message = ChatMessage.createVideoSendMessage(videoUri, thumbPath, videoLength, conversationId);
// Sets the chat type as one-to-one chat, group chat, or chatroom.
if (chatType == CHATTYPE_GROUP) {
message.setChatType(ChatType.GroupChat);
}
ChatClient.getInstance().chatManager().sendMessage(message);By default, when the recipient receives the message, the SDK downloads the thumbnail of the video message.
If you do not want the SDK to automatically download the video thumbnail, set ChatClient.getInstance().getOptions().setAutodownloadThumbnail as false, and to download the thumbnail, you need to call ChatClient.getInstance().chatManager().downloadThumbnail(message), and get the path of the thumbnail from the thumbnailLocalUri member in messageBody.
To download the actual video file, call ChatClient.getInstance().chatManager().downloadAttachment(message), and get the path of the video file from the getLocalUri member in messageBody.
// If you received a message with video attachment, you need download the attachment before you open it.
if (message.getType() == ChatMessage.Type.VIDEO) {
VideoMessageBody messageBody = (VideoMessageBody)message.getBody();
// Set Callback to know whether the download is finished.
final CallBack callback = new CallBack() {
public void onSuccess() {
EMLog.e(TAG, "onSuccess" );
// After the download finishes onSuccess, get the URI of the local file.
Uri videoLocalUri = messageBody.getLocalUri();
}
public void onError(final int error, String message) {
EMLog.e(TAG, "offline file transfer error:" + message);
}
public void onProgress(final int progress, String status) {
EMLog.d(TAG, "Progress: " + progress);
}
};
message.setMessageStatusCallback(callback);
ChatClient.getInstance().chatManager().downloadAttachment(message);
}Send and receive a file message
Refer to the following code example to create, send, and receive a file message:
// Set fileLocalUri as the URI of the file message on the local device.
ChatMessage message = ChatMessage.createFileSendMessage(fileLocalUri, conversationId);
// Sets the chat type as one-to-one chat, group chat, or chatroom.
if (chatType == CHATTYPE_GROUP){
message.setChatType(ChatType.GroupChat);
}
ChatClient.getInstance().chatManager().sendMessage(message);While sending a file message, refer to the following sample code to get the progress for uploading the attachment file:
// Calls setMessageStatusCallback to set the callback instance to listen for the state of messaging sending. You can update the message states in this callback.
message.setMessageStatusCallback(new CallBack() {
@Override
public void onSuccess() {
showToast("Message sending succeeds");
}
@Override
public void onError(int code, String error) {
showToast("Message sending fails");
}
// The status of sending the message. This only applies to sending attachment files.
@Override
public void onProgress(int progress, String status) {
}
});
ChatClient.getInstance().chatManager().sendMessage(message);When the recipient receives the message, refer to the following code example to get the attachment file:
NormalFileMessageBody fileMessageBody = (NormalFileMessageBody) message.getBody();
// Retrieves the file from the server.
String fileRemoteUrl = fileMessageBody.getRemoteUrl();
// Retrieves the file from the local device.
Uri fileLocalUri = fileMessageBody.getLocalUri();If the file does not exist in fileLocalUri, you can manually call ChatClient.getInstance().chatManager().downloadAttachment(message) to download the attachment, and call the message. SetMessageStatusCallback (the callback)to see if the download succeeds.
Send a location message
To send and receive a location message, you need to integrate a third-party map service provider. When sending a location message, you get the longitude and latitude information of the location from the map service provider; when receiving a location message, you extract the received longitude and latitude information and display the location on the third-party map.
// Sets the latitude and longitude information of the address.
ChatMessage message = ChatMessage.createLocationSendMessage(latitude, longitude, locationAddress, conversationId);
// Sets the chat type as one-to-one chat, group chat, or chatroom.
if (chatType == CHATTYPE_GROUP) {
message.setChatType(ChatType.GroupChat);
}
ChatClient.getInstance().chatManager().sendMessage(message);Send and receive a CMD message
CMD messages are command messages that instruct a specified user to take a certain action. The recipient deals with the command messages themselves.
CMD messages are not stored in the local database.Actions beginning with em_ and easemob:: are internal fields. Do not use them.
ChatMessage cmdMsg = ChatMessage.createSendMessage(ChatMessage.Type.CMD);
// Sets the chat type as one-to-one chat, group chat, or chat room.
if (chatType == CHATTYPE_GROUP){
cmdMsg.setChatType(ChatType.GroupChat);
}
String action="action1";
// You can customize the action.
CmdMessageBody cmdBody = new CmdMessageBody(action);
// Sets the message recipient: user ID of the recipient for one-to-one chat, group ID for group chat, or chat room ID for a chat room.
cmdMsg.setTo(conversationId);
cmdMsg.addBody(cmdBody);
ChatClient.getInstance().chatManager().sendMessage(cmdMsg);To notify the recipient that a CMD message is received, use a separate listener so that users can deal with the message differently.
MessageListener msgListener = new MessageListener()
{
// Occurs when the normal message is received
@Override
public void onMessageReceived(List messages) {
}
// Occurs when a CMD message is received
@Override
public void onCmdMessageReceived(List messages) {
}
}Send a customized message
The following code example shows how to create and send a customized message:
ChatMessage customMessage = ChatMessage.createSendMessage(ChatMessage.Type.CUSTOM);
// Set event as the customized message type, for example, gift.
event = "gift"CustomMessageBody;
customBody = new CustomMessageBody(event);
// The data type of params is Map.
customBody.setParams(params);
customMessage.addBody(customBody);
// Sets the message recipient: user ID of the recipient for one-to-one chat, group ID for group chat, or chat room ID for a chat room.
customMessage.setTo(to);
// Sets the chat type as one-to-one chat, group chat, or chat room
customMessage.setChatType(chatType);ChatClient.getInstance().chatManager().sendMessage(customMessage);Use message extensions
If the message types listed above do not meet your requirements, you can use message extensions to add attributes to the message. This can be applied in more complicated messaging use-cases.
ChatMessage message = ChatMessage.createTextSendMessage(content, conversationId);
// Adds message attributes.
message.setAttribute("attribute1", "value");message.setAttribute("attribute2", true);
// Sends the message
ChatClient.getInstance().chatManager().sendMessage(message);
// Retrieves the message attributes when receiving the message.
message.getStringAttribute("attribute1",null);message.getBooleanAttribute("attribute2", false)Forward a single message
You can use the Message#addBody and Message#setAttribute methods to create a new message identical to the original one, including its body and extended fields. After creating the message, you can forward it using the ChatManager#sendMessage method.
You can forward all types of messages in individual chats, chat groups, and chat rooms. For messages with attachments, there's no need to re-upload the attachments during forwarding.
However, if the original message expires (for example, deleted from the server due to storage limitations), the recipient can view the attachment address after forwarding but won't be able to download the attachment.
To forward a single message, refer to the following code:
// messageId: ID of the message for forwarding.
String messageId = "";
ChatMessage targetMessage = ChatClient.getInstance().chatManager().getMessage(messageId);
// to: The peer user ID for one-to-one chat, group ID for a group chat, and chat room ID for a room chat.
String to = "the conversationId you want to send to";
ChatMessage newMessage = ChatMessage.createSendMessage(targetMessage.getType());
newMessage.setTo(to);
// chatType: ChatMessage.ChatType.Chat for one-to-one chat, ChatMessage.ChatType.GroupChat for group chat, or ChatMessage.ChatType.ChatRoom.
// The default value is `ChatMessage.ChatType.Chat`. For a group chat, add the following line.
//newMessage.setChatType(ChatMessage.ChatType.GroupChat);
// Create a new message with the body and extension fields of the original message.
MessageBody targetMessageBody = targetMessage.getBody();
newMessage.addBody(targetMessageBody);
Map ext = targetMessage.ext();
//Traverse extension fields.
if (ext != null) {
for (Map.Entry entry : ext.entrySet()) {
Object value = entry.getValue();
if (value instanceof Long) {
newMessage.setAttribute(entry.getKey(), (long) value);
} else if (value instanceof Integer) {
newMessage.setAttribute(entry.getKey(), (int) value);
} else if (value instanceof String) {
newMessage.setAttribute(entry.getKey(), (String) value);
} else if (value instanceof Boolean) {
newMessage.setAttribute(entry.getKey(), (boolean) value);
} else if (value instanceof Double) {
newMessage.setAttribute(entry.getKey(), (double) value);
} else if (value instanceof Float) {
newMessage.setAttribute(entry.getKey(), (float) value);
} else if (value instanceof JSONArray) {
newMessage.setAttribute(entry.getKey(), (JSONArray) value);
} else if (value instanceof JSONObject) {
newMessage.setAttribute(entry.getKey(), (JSONObject) value);
}
}
}
ChatClient.getInstance().chatManager().sendMessage(newMessage);Forward multiple messages
Supported types for forwarded messages include text, images, audio & video files, attachment, and custom messages. A user can create a combined message with a list of original messages and send it. When receiving a combined message, the recipient can select it and other messages to create a new layered combined message. A combined message can contain up to 10 layers of messages, with at most 300 messages at each layer.
To forward and receive combined messages, refer to the following code:
-
Create a combined message using multiple message IDs:
ChatMessage message = ChatMessage.createCombinedSendMessage(title, summary, compatibleText, msgIds, toChatUsername); // Set the chat type: // One-to-one chat: ChatMessage.ChatType.Chat // Group chat: ChatMessage.GroupChat // Room chat: ChatMessage.ChatRoom message.setChatType(ChatMessage.ChatType.Chat); // Send the message ChatClient.getInstance().chatManager().sendMessage(message); -
Download and parse combined messages:
ChatClient.getInstance().chatManager().downloadAndParseCombineMessage(combinedMessage, new ValueCallBack>() { @Override public void onSuccess(List value) { } @Override public void onError(int error, String errorMsg) { } });
Modify sent messages
For messages that have been sent successfully, the SDK supports modifying the message content.
- Before SDK v1.4.0, you could only modify text messages sent in one-to-one chats and chat groups.
- Since SDK v1.4.0, you can modify various message types sent in one-to-one chats, chat groups, and chat rooms:
- Text and custom messages: modify both the message body and the extension fields (
ext). - File, video, voice, image, location, and combined messages: modify the extension fields (
ext) only. - Command messages: modification is not supported.
- Text and custom messages: modify both the message body and the extension fields (
There is no time limit for modifying a message, that is, it can be modified as long as the message is still stored on the server. After the message is modified, the message life cycle, that is, its storage time on the server, is recalculated. For example, a message can be stored on the server for 180 days, and the user modifies it on the 30th day after the message was sent. Instead of remaining 150 days, the message can be now stored on the server for 180 days after successful modification.
In the modified message, the message ID remains unchanged. The message body, the extension fields, or both are edited and the following items are added:
- The operator ID of the user performing the action.
- The operation time that indicates when the message was edited.
- The number of times a message is edited (up to 10 times).
For the edited message, other information included in the message, such as the message sender and recipient, remains unchanged.
To modify a sent message, refer to the following code:
-
Call
asyncModifyMessagewith the message ID, the new message body, and the new extension fields:// Text message: you can modify both the message body and the message extension fields. TextMessageBody textBody = new TextMessageBody("new content"); Map<String, Object> ext = new HashMap<>(); ext.put("newkey", "new value"); // textBody and ext cannot both be null. ChatClient.getInstance().chatManager().asyncModifyMessage(this.messageId, textBody, ext, new ValueCallBack<ChatMessage>() { @Override public void onSuccess(ChatMessage value) { // Modification succeeded. } @Override public void onError(int error, String errorMsg) { // Modification failed. } }); // Custom message: you can modify both the message body and the message extension fields. CustomMessageBody customBody = new CustomMessageBody("new action"); Map<String, Object> customExt = new HashMap<>(); customExt.put("newkey1", "new value 1"); customExt.put("newkey2", 123); ChatClient.getInstance().chatManager().asyncModifyMessage(this.messageId, customBody, customExt, new ValueCallBack<ChatMessage>() { @Override public void onSuccess(ChatMessage value) { // Modification succeeded. } @Override public void onError(int error, String errorMsg) { // Modification failed. } }); // File, video, voice, image, location, or combined message: you can modify the extension fields only. Pass null as the message body. Map<String, Object> attachmentExt = new HashMap<>(); attachmentExt.put("newkey1", false); attachmentExt.put("newkey2", "new value"); ChatClient.getInstance().chatManager().asyncModifyMessage(this.messageId, null, attachmentExt, new ValueCallBack<ChatMessage>() { @Override public void onSuccess(ChatMessage value) { // Modification succeeded. } @Override public void onError(int error, String errorMsg) { // Modification failed. } }); -
Receive notification of messages modified by other users:
// Create a MessageListener object MessageListener messageListener = new MessageListener() { //…… @Override public void onMessageContentChanged(ChatMessage messageModified, String operatorId, long operationTime) { // int operationCount = messageModified.getBody().operationCount(); // operatorId and operationTime can also be obtained as follows: // String id = messageModified.getBody().operatorId(); // long time = messageModified.getBody().operationTime(); } }; // Register the messageListener ChatClient.getInstance().chatManager().addMessageListener(messageListener); // Remove the messageListener ChatClient.getInstance().chatManager().removeMessageListener(messageListener);
For further details see Sent message modification limitations.
Next steps
After implementing sending and receiving messages, you can refer to the following documents to add more messaging functionalities to your app:
