Skip to main content

You are viewing Agora Docs forBeta products and features. Switch to Docs

Message channel quickstart

You use Signaling SDK to add low-latency, high-concurrency signaling and synchronization capabilities for real-time systems into your app. You can also use Signaling to enhance the user experience for Video Calling, Voice Calling, Interactive Live Streaming, and Broadcast Streaming.

This page shows you how to easily integrate pub-sub messaging into your app using Signaling SDK. For information about stream channels where users communicate using topics in a channel, see Stream channel quickstart.

Understand the tech

Pub-sub is the simplest form of messaging. Signaling creates a channel when a user subscribes to it. Your app listens for events which contain messages users publish to a channel.

You use an authentication token to authenticate a user with Signaling and join a channel. Each token is bound to a single user UID. This means that only one concurrent user may login from an instance of your app.

To create a pub-sub session for Signaling , implement the following steps in your app:

In this guide you retrieve a temporary token from a Token Generator. To understand how to create an authentication server for development purposes, see Implement the authentication workflow. To develop your own token generator and integrate it into your production IAM system, read Token generators.

Prerequisites

In order to follow this procedure you must:

  • Android Studio 4.1 or higher.
  • Android SDK API Level 24 or higher.
  • A mobile device that runs Android 4.1 or higher.
  • An Agora account and project.

  • A computer with Internet access.

    Ensure that no firewall is blocking your network communication.

  • Raise a support ticket to activate Signaling. In your ticket, please provide the following details:
    • Your Agora Customer Id
    • The project VID for which you want Signaling enabled
    • The App Id for the project in which you want to integrate Signaling
    • Do you require Stream Channel activation: Yes or No
    • Do you need data storage (Storing user and channel attributes as metadata): Yes or No
    • Your geographical region for product activation: US, EU, APAC (excluding China), or China
      The Agora support team confirms activation through a ticket update.
Note
  • Signaling 2.x is an enhanced version compared to 1.x with a wide range of new features. It follows a new pricing structure. Please visit the Pricing page for details.
  • The beta version of Signaling 2.x is offered free of charge for testing its features and performance for the duration of its public beta.

Project setup

Agora supplies a single GitHub reference repository. This runnable and testable repo contains all the code used in this Signaling SDK documentation. To create a Signaling SDK development environment:

  1. Clone the Signaling SDK reference app to <samples-root> on your development environment:

    Navigate to your <samples-root> folder and run the following command:


    _1
    git clone https://github.com/AgoraIO/video-sdk-samples-android.git

  2. Open the reference app in Android Studio:

    From the File menu select Open... then navigate to <samples-root>/video-sdk-samples-android/android-reference-app and click OK. Android Studio loads the project and Gradle sync downloads the dependencies.

Implement Signaling

Best practice is to separate the Signaling workflows from your UI implementation. The Signaling SDK sample project implements the Signaling business logic in the SignalingManager class. This class encapsulates the Signaling engine instance and core functionality such as logging in to Signaling, sending a message, listening for messages from other users and logging out.

The following code examples show how to implement these steps in your app:

Declare the variable to hold a Signaling Engine instance

You use these objects to communicate with Signaling.


_1
protected var signalingEngine: RtmClient? = null

Define configuration parameters

Best practice is to keep these variables separate from your code. For example, in the Signaling reference app, we use the following JSON configuration file.


_18
{
_18
"uid": "1",
_18
"appId": "",
_18
"channelName": "",
_18
"token": "",
_18
"proxyUrl": "http://localhost:8080/",
_18
"serverUrl": "<URL to a token server>",
_18
"tokenExpiryTime": "600",
_18
"encryptionMode": 1,
_18
"salt": "",
_18
"cipherKey": "",
_18
"presenceTimeout": 300,
_18
"logUpload": false,
_18
"logLevel": "debug",
_18
"cloudProxy": true,
_18
"useStringUserId": false,
_18
"rtcToken": ""
_18
}

Configure a Signaling Engine instance

Your code reads the parameters from the configuration file, initializes the SignalingEngine instance and adds the event listeners.


_15
protected open fun setupSignalingEngine(uid: Int): Boolean {
_15
try {
_15
val rtmConfig = RtmConfig.Builder(appId, uid.toString())
_15
.presenceTimeout(config!!.optString("presenceTimeout").toInt())
_15
.useStringUserId(false)
_15
.eventListener(eventListener)
_15
.build()
_15
signalingEngine = RtmClient.create(rtmConfig)
_15
localUid = uid
_15
} catch (e: Exception) {
_15
notify(e.toString())
_15
return false
_15
}
_15
return true
_15
}

Handle and respond to Signaling events


_41
protected open val eventListener: RtmEventListener = object : RtmEventListener {
_41
override fun onMessageEvent(eventArgs: MessageEvent) {
_41
// Receives Message Events
_41
mListener?.onSignalingEvent("Message", eventArgs) // notify the UI
_41
}
_41
_41
override fun onPresenceEvent(eventArgs: PresenceEvent) {
_41
// Receives Presence Events
_41
if (eventArgs.eventType == RtmConstants.RtmPresenceEventType.SNAPSHOT) {
_41
channelType = eventArgs.channelType
_41
}
_41
mListener?.onSignalingEvent("Presence", eventArgs)
_41
}
_41
_41
override fun onTopicEvent(eventArgs: TopicEvent) {
_41
// Receives Topic Events
_41
mListener?.onSignalingEvent("Topic", eventArgs)
_41
}
_41
_41
override fun onLockEvent(eventArgs: LockEvent) {
_41
// Receives Lock Events
_41
mListener?.onSignalingEvent("Lock", eventArgs)
_41
}
_41
_41
override fun onStorageEvent(eventArgs: StorageEvent) {
_41
// Receives Storage Events
_41
mListener?.onSignalingEvent("Storage", eventArgs)
_41
}
_41
_41
override fun onConnectionStateChanged(
_41
channelName: String?,
_41
state: RtmConstants.RtmConnectionState?,
_41
reason: RtmConstants.RtmConnectionChangeReason?
_41
) {
_41
super.onConnectionStateChanged(channelName, state, reason)
_41
}
_41
_41
override fun onTokenPrivilegeWillExpire(channelName: String) {
_41
// Receives Token Privilege Will Expire events
_41
}
_41
}

Login to Signaling

In Signaling, each token is specific to a user ID. If uid and token do not match, your user cannot initiate Signaling Engine.


_23
fun login(uid: Int, token: String): Int {
_23
if (signalingEngine == null ) {
_23
setupSignalingEngine(uid)
_23
}
_23
_23
signalingEngine?.login(token, object : ResultCallback<Void?> {
_23
override fun onFailure(errorInfo: ErrorInfo?) {
_23
if (errorInfo?.errorCode == RtmConstants.RtmErrorCode.DUPLICATE_OPERATION) {
_23
isLoggedIn = true
_23
logout()
_23
}
_23
notify("Login failed:\n"+ errorInfo.toString())// Handle failure
_23
}
_23
_23
override fun onSuccess(responseInfo: Void?) {
_23
localUid = uid
_23
isLoggedIn = true
_23
notify("Successfully logged in")
_23
mListener?.onLoginLogout(isLoggedIn) // notify the UI
_23
}
_23
})
_23
return 0
_23
}

Subscribe to a channel

To start receiving messages and event notifications from a channel, you call subscribe.


_17
fun subscribe(channelName: String): Int {
_17
// Subscribe to a channel
_17
val subscribeOptions = SubscribeOptions(true, true, true, true)
_17
_17
signalingEngine?.subscribe(channelName, subscribeOptions, object: ResultCallback<Void?> {
_17
override fun onFailure(errorInfo: ErrorInfo?) {
_17
notify("Subscribe failed:\n"+ errorInfo.toString())
_17
}
_17
_17
override fun onSuccess(responseInfo: Void?) {
_17
isSubscribed = true
_17
mListener?.onSubscribeUnsubscribe(isSubscribed)
_17
notify("Subscribed to channel: $channelName")
_17
}
_17
})
_17
return 0
_17
}

Send a message

Messages are published directly to a channel.


_15
fun publishChannelMessage (message: String): Int {
_15
val publishOptions = PublishOptions()
_15
_15
signalingEngine?.publish(channelName, message, publishOptions, object: ResultCallback<Void?> {
_15
override fun onFailure(errorInfo: ErrorInfo?) {
_15
notify("Failed to send message:\n"+ errorInfo.toString())
_15
}
_15
_15
override fun onSuccess(responseInfo: Void?) {
_15
notify("Message sent")
_15
}
_15
})
_15
_15
return 0
_15
}

You setup the event handlers for messages received from other users during Signaling Engine initiation.

Unsubscribe from a channel

When you no longer need to receive channel notifications, call unsubscribe.


_14
fun unsubscribe(channelName: String): Int {
_14
signalingEngine?.unsubscribe(channelName, object: ResultCallback<Void?> {
_14
override fun onFailure(errorInfo: ErrorInfo?) {
_14
notify("Unsubscribe failed:\n"+ errorInfo.toString())
_14
}
_14
_14
override fun onSuccess(responseInfo: Void?) {
_14
isSubscribed = false
_14
notify("Unsubscribed from channel: $channelName")
_14
mListener?.onSubscribeUnsubscribe(isSubscribed)
_14
}
_14
})
_14
return 0
_14
}

Logout of Signaling

Had enough of the conversation? Just logout.


_24
open fun logout() {
_24
if (!isLoggedIn) {
_24
notify("You need to login first")
_24
} else {
_24
// To leave a channel, call the `leaveChannel` method
_24
signalingEngine?.logout(object: ResultCallback<Void?> {
_24
override fun onFailure(errorInfo: ErrorInfo?) {
_24
notify("Logout failed:\n"+ errorInfo.toString())
_24
}
_24
_24
override fun onSuccess(responseInfo: Void?) {
_24
isLoggedIn = false
_24
if (isSubscribed) {
_24
isSubscribed = false
_24
mListener?.onSubscribeUnsubscribe(isSubscribed)
_24
}
_24
notify("Logged out successfully")
_24
mListener?.onLoginLogout(isLoggedIn)
_24
// Destroy the engine instance
_24
destroySignalingEngine()
_24
}
_24
})
_24
}
_24
}

You have just implemented the Signaling workflow. Yes, it's as easy as that.

Test Signaling

This section explains how to run the reference app and see the corresponding features in an app.

In Signaling, each authentication token you create is specific for a user ID. You create a token for each user in the channel. Each user must log in from a different instance of your app. To test Signaling, you run 2 or more instances of your app. When you call login using Signaling SDK, ensure that the UID is the same as you used to create the token.

For each user in your tests:

  1. Configure the project

    1. Open the file <samples-root>/signaling-manager/src/main/res/raw/config.json

    2. Replace the value for appId with the value from Agora Console.
    3. Generate an RTM token using your uid.
    4. Replace the value for token with the Signaling token.
    5. Replace the value for uid with the the value you used to generate the token.
    6. Ensure that the channelName is filled in. The channel name can be any string.
  2. Run the reference app

    1. In Android Studio, connect a physical Android device to your development machine.
    2. Click Run to start the app.
    3. A moment later you see the project installed on your device.
  3. Test the basic functionality:

    1. Open a new instance of the reference app.
    2. Choose the SDK quickstart example.
    3. Log in to Signaling.
    4. Subscribe to a channel, then send and receive messages.

Reference

This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

Token expiration

After a token expires, you call the logout method to log out of Signaling, then use the new Token to create an RTM instance, and then call the login method to log in to Signaling again.

For more information, see:

Signaling