Skip to main content
Version: 2.x.x

Chat using PubSub - Android

For communication or any kind of messaging between participants, VideoSDK provides the pubSub class, which uses the Publish-Subscribe mechanism and can be used to develop a wide variety of functionalities. For example, participants could use it to send chat messages to each other, share files or other media, or even trigger actions like muting or unmuting audio or video.

Now we will see how we can use PubSub to implement Chat functionality. If you are not familiar with the PubSub mechanism and the pubSub class, you can follow this guide.

caution

publish(), subscribe() and unsubscribe() all require a joined meeting. They throw an Exception if the meeting has not reached the CONNECTED state or is RECONNECTING. The examples below open the chat screen during an ongoing meeting, so the meeting is already connected — if instead you subscribe as part of your join flow, do it from onMeetingJoined().

Implementing Chat

Group Chat

  1. First step in creating a group chat is choosing the topic which all the participants will publish and subscribe to send and receive the messages. We will be using CHAT as the topic for this one.
  2. On the send button, publish the message that the sender typed in the EditText field.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import live.videosdk.rtc.android.Meeting
import live.videosdk.rtc.android.listeners.PubSubMessageListener
import live.videosdk.rtc.android.model.PubSubPublishOptions

class ChatActivity : AppCompatActivity() {
// Meeting
var meeting: Meeting? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat)

/**
* Here, we have created 'MainApplication' class, which extends android.app.Application class.
* It has Meeting property and getter and setter methods of Meeting property.
* In your android manifest, you must declare the class implementing android.app.Application
* (add the android:name=".MainApplication" attribute to the existing application tag):
* In MainActivity.kt, we have set Meeting property.
*
* For Example: (MainActivity.kt)
* var meeting = VideoSDK.initMeeting(context, meetingId, ParticipantName, micEnabled, webcamEnabled, paricipantId, mode, multiStream, customTrack, metaData, signalingBaseUrl,preferredProtocol)
* (this.application as MainApplication).meeting = meeting
*/

// Get Meeting
meeting = (this.application as MainApplication).meeting

findViewById<View>(R.id.btnSend).setOnClickListener { sendMessage() }
}

private fun sendMessage() {
// get message from EditText
val message: String = etmessage.text.toString()
if (!TextUtils.isEmpty(message)) {
val publishOptions = PubSubPublishOptions()
publishOptions.isPersist = true

// Sending the Message using the publish method
try {
meeting!!.pubSub.publish("CHAT", message, publishOptions)

// Clearing the message input
etmessage.setText("")
} catch (e: Exception) {
// Meeting is not connected yet, or is reconnecting
Toast.makeText(
this@ChatActivity, "Can't send right now, please try again",
Toast.LENGTH_SHORT
).show()
}
} else {
Toast.makeText(
this@ChatActivity, "Please Enter Message",
Toast.LENGTH_SHORT
).show()
}
}
}
  1. Next step would be to display the messages others send. For this we have to subscribe to that topic i.e CHAT and display all the messages.
class ChatActivity : AppCompatActivity() {

// PubSubMessageListener
val pubSubMessageListener = object : PubSubMessageListener {
override fun onMessageReceived(message: PubSubMessage) {
Log.d("VideoSDK", "onMessageReceived: ${message.message}")
}
override fun onOldMessagesReceived(messages: List<PubSubMessage>) {
// Persisted message list
Log.d("VideoSDK", "onOldMessagesReceived: $messages")
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat)

//...

// Subscribe for 'CHAT' topic
try {
meeting!!.pubSub.subscribe("CHAT", pubSubMessageListener)
} catch (e: Exception) {
// Meeting is not connected yet, or is reconnecting
Log.d("VideoSDK", "subscribe failed: ${e.message}")
}
}
}
  1. Final step in the group chat would be unsubscribe to that topic, which you had previously subscribed but no longer needed. Here we are unsubscribe to CHAT topic on activity destroy.
caution

If the activity is destroyed after the meeting has already ended or while it is RECONNECTING, unsubscribe() throws an Exception. Since onDestroy() frequently runs after the meeting is over, guard the call as shown below.

class ChatActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat)

//...
}

override fun onDestroy() {
// Unsubscribe for 'CHAT' topic
try {
meeting?.pubSub?.unsubscribe("CHAT", pubSubMessageListener)
} catch (e: Exception) {
// Meeting already ended or reconnecting — nothing left to unsubscribe from
Log.d("VideoSDK", "unsubscribe skipped: ${e.message}")
}
super.onDestroy()
}
}

Private Chat

In the above example, if you want to convert into the private chat between two participants, then all you have to do is pass sendOnly parameter in PubSubPublishOptions.

class ChatActivity : AppCompatActivity() {

//..

private fun sendMessage() {
// get message from EditText
val message: String = etmessage.text.toString()
if (!TextUtils.isEmpty(message)) {
val publishOptions = PubSubPublishOptions()
publishOptions.isPersist = true
// Pass the participantId of the participant to whom you want to send the message.
val sendOnly: Array<String> = arrayOf("xyz")
publishOptions.setSendOnly(sendOnly)

// Sending the Message using the publish method
try {
meeting!!.pubSub.publish("CHAT", message, publishOptions)

// Clearing the message input
etmessage.setText("")
} catch (e: Exception) {
Log.d("VideoSDK", "publish failed: ${e.message}")
}
} else {
Toast.makeText(
this@ChatActivity, "Please Enter Message",
Toast.LENGTH_SHORT
).show()
}
}
}

High-volume Chat

In a large room a chat topic can produce more messages than the UI can render. Subscribe with PubSubSubscribeOptions to cap how much history you load and to control what happens when live messages outpace your consumer.

// Backing state for your chat UI
val chatMessages = mutableStateListOf<PubSubMessage>()

val pubSubMessageListener = object : PubSubMessageListener {
override fun onMessageReceived(message: PubSubMessage) {
chatMessages.add(message)
}

override fun onBatchReceived(messages: List<PubSubMessage>) {
// One callback per live batch — add in bulk instead of one at a time
chatMessages.addAll(messages)
}

override fun onOldMessagesReceived(messages: List<PubSubMessage>, info: PubSubHistoryInfo) {
chatMessages.addAll(messages)
if (info.isLast) isHistoryLoaded = true
}

override fun onMessageDrop(info: PubSubMessageDropInfo) {
Log.w("VideoSDK", "chat is running hot — dropped ${info.droppedCount} messages")
}
}

private fun subscribeChat() {
val options = PubSubSubscribeOptions()
options.oldMessageLimit = 100 // only load the last 100 messages
options.realtimeOverflow = PubSubSubscribeOptions.RealtimeOverflow.QUEUE
options.maxQueue = 70

try {
meeting!!.pubSub.subscribe("CHAT", pubSubMessageListener, options)
} catch (e: IllegalArgumentException) {
// Invalid options, or this listener is already subscribed to the topic
Log.d("VideoSDK", "subscribe rejected: ${e.message}")
} catch (e: Exception) {
// Meeting is not connected yet, or is reconnecting
Log.d("VideoSDK", "subscribe failed: ${e.message}")
}
}

Handling failures

If the server rejects a chat operation, the SDK reports it through onError on the MeetingEventListener rather than by throwing: code 4087 (PUBSUB_PUBLISH_FAILED) when a message could not be delivered, 4088 (PUBSUB_SUBSCRIBE_FAILED) when the subscription was rejected, and 4089 (PUBSUB_UNSUBSCRIBE_FAILED). See the Error Events guide.

Downloading Chat Messages

All the messages from PubSub published with persist : true can be downloaded as a .csv file. This file will be available in the VideoSDK dashboard as well as through the Sessions API.

API Reference

The API references for all the methods and events utilised in this guide are provided below.

Got a Question? Ask us on discord