PubSub - Android
PubSub is a short acronym for Publish-Subscribe mechanism. This mechanism is used to send and recieve messages from a particular topic. As the name suggests, for someone to send a message, they have to specify the topic and the message which should be published and for someone to receive a message, they should be subscribed to that topic.
Here is a visual to better understand publish-subscribe mechanism.

pubSub
In order to use PubSub in meeting, VideoSDK provides a class pubSub which allows you to subscribe to any topic and publish to any topic allowing to pass on messages and instructions during the meeting easily.
publish()
- This method is used for publishing message of specific topic.
- This method can be accessed from the
pubSubclass, which is subclass ofMeetingclass. - This method will accept following parameters as input:
topic: This will be the topic for which you are publishing a message.message: This will be the actual message to be published. It has to be inStringformat.options: This is an object ofPubSubPublishOptionswhich specifies the options for publish.PubSubPublishOptionshas 2 properties.persist:persistoffered the option of keeping the message around for the duration of the session. Whenpersistis set totrue, that message will be retained for upcoming participants and will be available in VideoSDK Session Dashboard with.CSVformat after completion of session.sendOnly: If you want to send a message to specific participants, you can pass their respectiveparticipantIdin form ofString[]. If you don't provide any IDs or pass anullvalue, the message will be sent to all participants by default. This is optional parameter.
payload: If you need to include additional information along with a message, you can pass here asJSONObject. This is optional parameter.
- Kotlin
- Java
private fun sendMessage() {
// publish message
val options = PubSubPublishOptions()
options.isPersist = true
try {
meeting!!.pubSub.publish("CHAT", "Hello Everyone!", options)
} catch (e: Exception) {
// Meeting is not connected yet, or is reconnecting
Log.d("VideoSDK", "publish failed: ${e.message}")
}
}
protected void sendMessage() {
// publish message
PubSubPublishOptions options = new PubSubPublishOptions();
options.setPersist(true);
try {
meeting.pubSub.publish("CHAT", "Hello Everyone!", options);
} catch (Exception e) {
// Meeting is not connected yet, or is reconnecting
Log.d("VideoSDK", "publish failed: " + e.getMessage());
}
}
subscribe()
-
This method is used to subscribe for particular topic.
-
This method will accept following parameters as input:
-
topic: This will be the topic to be subscribed. -
listener: This is an object ofPubSubMessageListenercontaining the following callbacks:-
onMessageReceived(message)— Called for every realtime message as soon as it is received.- Use this when: You want to handle each message individually, such as appending it to a chat, triggering a notification, or updating message-specific state.
- Don't use this when: The topic is high-throughput / large-scale (busy chats, live reactions, cursor positions). Firing once per message causes excessive UI updates — use
onBatchReceived()instead.
-
onBatchReceived(messages)— Called once per live batch with all messages of that batch. These are the same messages delivered throughonMessageReceived(), only grouped — use one path or the other, not both.- Use this when: Handling messages in bulk is cheaper, such as one UI update or one database transaction per batch instead of one per message.
- Note: Batched delivery is enabled per meeting by the server configuration, not by subscribe options. Where it is not enabled this callback never fires and live messages arrive solely through
onMessageReceived(), so handle both paths.
-
onOldMessagesReceived(messages)— Called with the topic's persisted history in batches, once per batch with a partial list. Only messages published withpersistenabled are delivered here. Append each batch to what you already hold rather than replacing it. A topic with no persisted history never fires this callback.- Use this when: Loading chat history or restoring previously sent messages when a participant joins the meeting.
-
onOldMessagesReceived(messages, info)— The same history replay, plus batch metadata:info.isLast()istrueon the final batch.- Use this when: You need to know that history replay is complete — for example, to hide a loader once the full history is rendered.
-
onMessageDrop(info)— Called when incoming realtime messages cannot be delivered. This can happen:- because the client cannot keep up with the incoming message rate (for example, due to a slow network or high CPU usage), causing overflow, or
- because
newMessageLimitis configured, which intentionally limits how many realtime messages are delivered every 500 ms. Any messages exceeding the configured limit within that window are discarded.
info.getDroppedCount()contains the number of messages that were not delivered.- Use this when: Notifying users that some realtime messages were missed, monitoring the effects of
newMessageLimit, or logging dropped messages for debugging and observability.
-
-
options: This is an object ofPubSubSubscribeOptionswhich specifies the options for subscribe. This is optional parameter.PubSubSubscribeOptionshas 4 properties.oldMessageLimit: This limits how many persisted messages are fetched when you subscribe. Passnullfor all history,0for none, orNfor the last N messages. Defaults tonull.realtimeOverflow: This decides what happens when live messages arrive faster than they can be delivered.QUEUEbuffers them and delivers them paced, dropping the oldest on overflow;DROPkeeps only the newest messages of each delivery window. Defaults toQUEUE.maxQueue: This is the buffer ceiling for live messages underQUEUE. It is only valid withQUEUE, and combining it withDROPwill throw anIllegalArgumentException. If you don't provide any value, the server decides.newMessageLimit: This caps how many new messages this subscriber accepts per server batching window (500 ms). If you don't provide any value, it is unlimited.
-
The object of PubSubMessage contains following properties:
id: This is the unique id of the message. TypeString.message: This will be the actual message that was sent. TypeString.senderId: This represents theparticipantIdof the participant who sent the message. TypeString.senderName: This represents thedisplayNameof the participant who sent the message. TypeString.timestamp: This will be the timestamp for when the message was published. Typelong.topic: This will be the name of the topic the message was published to. TypeString.payload: This will be the data that you have sent along with the message. TypeJSONObject.
- Kotlin
- Java
val pubSubMessageListener = object : PubSubMessageListener {
override fun onMessageReceived(message: PubSubMessage) {
Log.d("#message", "onMessageReceived: ${message.message}")
}
override fun onOldMessagesReceived(messages: List<PubSubMessage>) {
Log.d("#message", "onOldMessagesReceived: $messages")
}
override fun onOldMessagesReceived(messages: List<PubSubMessage>, info: PubSubHistoryInfo) {
Log.d("#message", "onOldMessagesReceived: ${messages.size} messages, isLast: ${info.isLast}")
}
override fun onBatchReceived(messages: List<PubSubMessage>) {
Log.d("#message", "onBatchReceived: ${messages.size} messages")
}
override fun onMessageDrop(info: PubSubMessageDropInfo) {
Log.d("#message", "onMessageDrop: dropped ${info.droppedCount} messages")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
//...
// 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}")
}
}
PubSubMessageListener pubSubMessageListener = new PubSubMessageListener() {
@Override
public void onMessageReceived(PubSubMessage message) {
Log.d("#message", "onMessageReceived: " + message.getMessage());
}
@Override
public void onOldMessagesReceived(List<PubSubMessage> messages) {
Log.d("#message", "onOldMessagesReceived: " + messages);
}
@Override
public void onOldMessagesReceived(List<PubSubMessage> messages, PubSubHistoryInfo info) {
Log.d("#message", "onOldMessagesReceived: " + messages.size() + " messages, isLast: " + info.isLast());
}
@Override
public void onBatchReceived(List<PubSubMessage> messages) {
Log.d("#message", "onBatchReceived: " + messages.size() + " messages");
}
@Override
public void onMessageDrop(PubSubMessageDropInfo info) {
Log.d("#message", "onMessageDrop: dropped " + info.getDroppedCount() + " messages");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
//...
// Subscribe for 'CHAT' topic
try {
meeting.pubSub.subscribe("CHAT", pubSubMessageListener);
} catch (Exception e) {
// Meeting is not connected yet, or is reconnecting
Log.d("VideoSDK", "subscribe failed: " + e.getMessage());
}
}
unsubscribe()
-
This method is used to unsubscribe for particular topic.
-
This method will accept two parameters as input:
topic: This will be the topic to be unsubscribed.listener: This is an object ofPubSubMessageListener, which was passed insubscribe().
- Kotlin
- Java
fun unsubscribe() {
// Unsubscribe 'CHAT' topic
try {
meeting!!.pubSub.unsubscribe("CHAT", pubSubMessageListener)
} catch (e: Exception) {
// Meeting already ended or is reconnecting — nothing left to unsubscribe from
Log.d("VideoSDK", "unsubscribe skipped: ${e.message}")
}
}
public void unsubscribe(){
// Unsubscribe 'CHAT' topic
try {
meeting.pubSub.unsubscribe("CHAT", pubSubMessageListener);
} catch (Exception e) {
// Meeting already ended or is reconnecting — nothing left to unsubscribe from
Log.d("VideoSDK", "unsubscribe skipped: " + e.getMessage());
}
}
All three methods require a joined meeting and throw if it is not connected. See Errors for what each method throws and how server-side failures are reported.
Errors
PubSub reports failures through method exceptions (try / catch) or through MeetingEventListener.onError(JSONObject).
publish() throws:
IllegalStateException— "You must join the meeting before using PubSub." The meeting has not reachedCONNECTED(or has already left). Also firesonErrorwith code3001.IllegalStateException— "The meeting is reconnecting. Please try again once it reconnects." No error event is fired, because your app tracks state viaonMeetingStateChanged.IllegalStateException— "Operation Timeout." The PubSub connection is disconnected or closed, so the operation cannot proceed. Also firesonErrorwith code3013.
subscribe() throws:
IllegalArgumentException— "You already subscribed to this topic with same listener." One listener instance may hold at most one subscription per topic. Callunsubscribe()before re-subscribing, or create a second listener instance.IllegalArgumentException—oldMessageLimitis negative. It must benull(all history) or>= 0.IllegalArgumentException—maxQueueis less than1.IllegalArgumentException—maxQueuewas set whilerealtimeOverflowisDROP. It is only valid withQUEUE.IllegalArgumentException—newMessageLimitis negative. It must benullor>= 0.IllegalStateException— "You must join the meeting before using PubSub." The meeting has not reachedCONNECTED(or has already left). Also firesonErrorwith code3001.IllegalStateException— "The meeting is reconnecting. Please try again once it reconnects." No error event is fired, because your app tracks state viaonMeetingStateChanged.IllegalStateException— "Operation Timeout." The PubSub connection is disconnected or closed, so the operation cannot proceed. Also firesonErrorwith code3013.
Options are validated before initiating network operations, so an invalid configuration fails immediately.
unsubscribe() throws:
IllegalStateException— "You must join the meeting before using PubSub." The meeting has not reachedCONNECTED(or has already left). Also firesonErrorwith code3001.IllegalStateException— "The meeting is reconnecting. Please try again once it reconnects." No error event is fired, because your app tracks state viaonMeetingStateChanged.IllegalStateException— "Operation Timeout." The PubSub connection is disconnected or closed, so the operation cannot proceed. Also firesonErrorwith code3013.
Reported through onError:
| Code | Name | Description |
|---|---|---|
| 3001 | ERROR_ACTION_PERFORMED_BEFORE_MEETING_JOINED | A PubSub method was called before the meeting was joined. |
| 3012 | ERROR_GET_SERVER_CONFIG_FAILED | Unable to fetch server configuration during meeting join. |
| 3013 | OPERATION_TIMEOUT | PubSub operation timed out due to a disconnected or closed connection. |
| 4087 | PUBSUB_PUBLISH_FAILED | Message could not be delivered — server rejected it or connection dropped before acknowledgment. |
| 4088 | PUBSUB_SUBSCRIBE_FAILED | Server rejected the subscription. The listener is unregistered again. |
| 4089 | PUBSUB_UNSUBSCRIBE_FAILED | Server rejected the unsubscribe request. The listener is removed locally either way. |
Applications of PubSub
PubSub is a very powerful mechanism which can be used to do a lot of things which can make your meeting experience much more interactive. Some of the most common use cases that we have come across for PubSub during a meeting are listed below:
Chat: You can utilise this to develop various Chat features, such as Private Chat and Group Chat. You can follow our chat integration guide here.Raise Hand: You can allow attendees to raise their hands at any time during the meeting, informing everyone else that someone has done so.Layout Switching: You can change the meeting's layout for every participant at once during the meeting, such as from Grid layout to Spotlight or from Grid Layout to Sidebar.Poll: You may make polls, let users respond to them, and display the results at the end of a poll.Question Answer Session: You can also design interactive functionality that is question-and-answer based.
Downloading PubSub 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

