Skip to main content
Version: 2.x.x

Migration Guide

This guide covers what you need to change when upgrading the Android SDK from v1.x.x to v2.0.0. Most of the work is in PubSub, plus one rename in the onError payload.

Update the dependency first:

implementation 'live.videosdk:rtc-android-sdk:2.0.1'

1. PubSub methods now throw

publish(), subscribe() and unsubscribe() require a joined meeting. In v1.x.x an ill-timed call failed silently; in v2.0.0 it throws IllegalStateException.

The most common place this bites is an unsubscribe() in onDestroy(), which frequently runs after the meeting has already ended.

Key points:

  • Subscribe from onMeetingJoined() rather than from onCreate() of the activity that joins the meeting — at onCreate() time the meeting is not connected yet.

2. Persisted messages now arrive in batches

In v1.x.x, onOldMessagesReceived(List<PubSubMessage>) was called once with the topic's complete history. In v2.0.0 history is replayed in server-paced batches, so the same callback is called once per batch with a partial list.

Before

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

override fun onOldMessagesReceived(messages: List<PubSubMessage>) {
// Called once with the complete history — safe to replace
chatMessages.clear()
chatMessages.addAll(messages)
}

After

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

override fun onOldMessagesReceived(messages: List<PubSubMessage>, info: PubSubHistoryInfo) {
// Called once per batch — append, don't replace
chatMessages.addAll(messages)
if (info.isLast) {
// Every persisted message for this topic has now been delivered
isHistoryLoaded = true
}
}

Key points:

  • If you keep the one-argument onOldMessagesReceived(messages), it still receives every batch — but you must append rather than replace, and it carries no end-of-replay signal.
  • A topic with no persisted history fires no history callback at all. In v1.x.x it fired once with an empty list, so if you used that as a "replay finished" signal, move to info.isLast().
  • Use oldMessageLimit (see section 4) to cap how much history you fetch, or pass 0 to skip it entirely.

3. New PubSub listener callbacks, and all of them are optional

In v1.x.x, PubSubMessageListener declared two abstract methods, so every implementation had to provide both. In v2.0.0 the interface has five methods and all of them are default — implement only the ones you need.

val pubSubMessageListener = object : PubSubMessageListener {
// Existing — fires once per real-time message
override fun onMessageReceived(message: PubSubMessage) { }

// Existing — now called once per history batch
override fun onOldMessagesReceived(messages: List<PubSubMessage>) { }

// New — history batches, with an end-of-replay signal
override fun onOldMessagesReceived(messages: List<PubSubMessage>, info: PubSubHistoryInfo) { }

// New — the same real-time messages, grouped into a batch
override fun onBatchReceived(messages: List<PubSubMessage>) { }

// New — real-time messages shed because the buffer overflowed
override fun onMessageDrop(info: PubSubMessageDropInfo) { }
}

Key points:

  • Your existing v1.x.x listener still compiles — the two methods it implements are now optional rather than required.
  • On a busy topic, prefer onBatchReceived() over onMessageReceived() — one UI update per batch instead of one per message. They deliver the same messages, so use one or the other, not both.
  • Learn more: PubSubMessageListener

4. New subscribe() options

subscribe() gained a third parameter of type PubSubSubscribeOptions, used to control how much history you fetch and what happens when messages arrive faster than your app can drain them. The two-parameter overload is unchanged, so existing calls keep working.

val options = PubSubSubscribeOptions()
options.oldMessageLimit = 50 // fetch the 50 most recent persisted messages
options.realtimeOverflow = PubSubSubscribeOptions.RealtimeOverflow.QUEUE
options.maxQueue = 100 // only valid with QUEUE
meeting!!.pubSub.subscribe("CHAT", pubSubMessageListener, options)

Key points:

  • oldMessageLimitnull (default) fetches all history, 0 fetches none, N fetches the most recent N.
  • realtimeOverflowQUEUE (default) buffers messages so you receive them once you catch up, dropping the oldest on overflow and reporting the count through onMessageDrop(); DROP sheds the excess silently. Use QUEUE for chat where every message matters, DROP for high-frequency, low-value streams such as reactions or cursor positions.
  • maxQueue — the buffer ceiling under QUEUE. Combining it with DROP throws IllegalArgumentException.
  • newMessageLimit — caps how many live messages you accept per 500 ms delivery window. null or 0 means unlimited.
  • Invalid options are rejected synchronously, before any network work, so a bad configuration fails immediately rather than at runtime.
  • Learn more: PubSubSubscribeOptions

5. One subscription per listener, per topic

A listener instance may hold at most one subscription to a given topic. Subscribing the same listener to the same topic twice now throws IllegalArgumentException instead of silently registering it again.

What you should do:

  • Call unsubscribe() before re-subscribing the same listener — for example when an activity or fragment is re-created.
  • If you need to handle the same topic in two places, create a second listener instance and subscribe it separately.

6. New error codes

v2.0.0 adds seven error codes, delivered through MeetingEventListener.onError(JSONObject).

NameCodeMeaning
ERROR_GET_SERVER_CONFIG_FAILED3012Unable to fetch server configuration during meeting join.
OPERATION_TIMEOUT3013PubSub operation timed out due to a disconnected or closed connection.
FAILED_TO_OBSERVE_VALUE4085Failed to start observing the realtime store value.
FAILED_TO_STOP_OBSERVING_VALUE4086Failed to stop observing the realtime store value.
PUBSUB_PUBLISH_FAILED4087The message could not be delivered.
PUBSUB_SUBSCRIBE_FAILED4088The subscription was rejected; the listener is unregistered again.
PUBSUB_UNSUBSCRIBE_FAILED4089The unsubscribe was rejected; the listener is removed locally either way.

Key points:

  • These are reported asynchronously through onError, unlike the IllegalStateException in section 1, which is thrown at the call site.
  • Learn more: Meeting Error Codes

7. The onError payload key type is now name

The payload delivered to MeetingEventListener.onError(JSONObject) still carries code and message unchanged, but the constant-name key was renamed from type to name.

There is no compatibility alias. Code reading error.getString("type") will no longer find a value.

Before

override fun onError(error: JSONObject) {
val code = error.optInt("code")
val type = error.optString("type")
Log.e("#error", "Error $code ($type)")
}

After

override fun onError(error: JSONObject) {
val code = error.optInt("code")
val name = error.optString("name")
Log.e("#error", "Error $code ($name): ${error.optString("message")}")
}

Key points:

  • code and message are unchanged — only the constant-name key moved.
  • name holds the enum constant, for example "PUBSUB_SUBSCRIBE_FAILED", which is useful when correlating Android logs with your other platforms.
  • VideoSDK.getErrorCodes() still returns the name → code map.
  • Learn more: Error Events

API Reference

Got a Question? Ask us on discord