Skip to main content

SDK quickstart

This page shows you how to create your first Signaling app using Signaling SDK.

Understand the tech

Log in to Signaling

The login process includes:

  1. The app client requests a token from your app server.

  2. The app server returns the token to the app client.

  3. The app client logs in to Signaling with the token.

Peer-to-peer messaging

The peer messaging process includes:

  1. Client A sends a peer message to Signaling.

  2. Signaling sends the message to client B. Client B receives the peer message.

Channel messaging

The channel messaging process includes:

  1. Client A creates a channel and join the channel.

  2. Client B and client C joins the channel created by client A.

  3. Client A sends a channel message to Signaling.

  4. Signaling sends the channel message to client B and C. Client B and client C receives the message.

For an app client to join a channel, you need the following information:

  • The App ID: A randomly generated string provided by Agora for identifying your app. You can get the App ID from Agora Console.

  • The user ID: The unique identifier of a user. You need to specify the user ID yourself, and ensure that it is unique in the channel.

  • A token: In a test or production environment, your app client retrieves tokens from your server.

  • The channel name: A string that identifies the channel for the channel messaging.

Prerequisites

In order to follow this procedure you must have:

  • 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.

Project setup

Follow the steps to create the environment necessary to add Signaling into your app.

1. Create an Android project

Use Android Studio to create an Android project.

  1. Select the Project Template as Empty Activity.

  2. Enter RtmQuickstart as the Name.

  3. Enter com.example.rtmquickstart as the Package name.

  4. Select Language as Java.

2. Add permissions

Add the following permissions in the AndroidManifest.xml file.


_6
<uses-permission android:name="android.permission.INTERNET" />
_6
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
_6
// As of v1.4.10, you don't need to add the WRITE_EXTERNAL_STORAGE permission:
_6
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
_6
// As of v1.4.9, you also need to add the following permission to check the connection status of the WIFI network:
_6
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

If your app targets Android 6.0 or higher, you need to dynamically apply for the above permissions, see Request app permissions for details.

If your app targets Android 10 or higher, refer to Privacy changes in Android 10.

3. Prevent code obfuscation

Add the following line in the app/proguard-rules.pro file to prevent code obfuscation:


_1
-keep class io.agora.**{*;}

If an error is reported, you can change the code as:


_1
-keep class io.agora**{\*;}

4. Integrate the SDK

Integrate the Agora Signaling Android SDK into your project with Maven Central. For more integration methods, see Other approaches to integrate the SDK.

  1. In /Gradle Scripts/build.gradle(Project: <projectname>), add the following lines to add the Maven Central dependency:


    _14
    buildscript {
    _14
    repositories {
    _14
    _14
    mavenCentral()
    _14
    }
    _14
    _14
    }
    _14
    _14
    allprojects {
    _14
    repositories {
    _14
    _14
    mavenCentral()
    _14
    }
    _14
    }

  2. In /Gradle Scripts/build.gradle(Module: <projectname>.app), add the following lines to integrate the Agora Signaling Android SDK into your Android project:


    _7
    _7
    dependencies {
    _7
    _7
    // For x.y.z, fill in a specific SDK version number. For example, 1.4.9.
    _7
    // Get the latest version number through the release notes.
    _7
    implementation 'io.agora.rtm:rtm-sdk:x.y.z'
    _7
    }

Implement Signaling

This section shows how to use the Agora Signaling SDK to implement Signaling into your app step by step.

1. Implement the UI and resource files

To help you quickly understand and implement functions of Agora Signaling, this section shows how to implement the following functions in an Activity with minimum effort:

  • Log in to and log out of Agora Signaling

  • Join and leave a channel

  • Send and receive peer-to-peer or channel messages

  • See the changes in connection state of the user

    1. Open the app/res/layout/activity_main.xml file with Android Studio, edit it in Code mode, and replace the contents of the file with the following:


      _161
      <?xml version="1.0" encoding="utf-8"?>
      _161
      <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
      _161
      xmlns:app="http://schemas.android.com/apk/res-auto"
      _161
      xmlns:tools="http://schemas.android.com/tools"
      _161
      android:layout_width="match_parent"
      _161
      android:layout_height="match_parent"
      _161
      android:background="#FFFFFF"
      _161
      tools:context=".MainActivity">
      _161
      _161
      <Button
      _161
      android:id="@+id/login_button"
      _161
      android:layout_width="wrap_content"
      _161
      android:layout_height="wrap_content"
      _161
      android:layout_marginStart="4dp"
      _161
      android:layout_marginTop="40dp"
      _161
      android:layout_marginEnd="132dp"
      _161
      android:onClick="onClickLogin"
      _161
      android:text="@string/login_button"
      _161
      app:layout_constraintEnd_toEndOf="parent"
      _161
      app:layout_constraintHorizontal_bias="0.0"
      _161
      app:layout_constraintStart_toEndOf="@+id/uid"
      _161
      app:layout_constraintTop_toTopOf="parent" />
      _161
      _161
      <EditText
      _161
      android:id="@+id/uid"
      _161
      android:layout_width="150dp"
      _161
      android:layout_height="40dp"
      _161
      android:layout_marginStart="37dp"
      _161
      android:layout_marginTop="40dp"
      _161
      android:autofillHints=""
      _161
      android:hint="@string/uid"
      _161
      android:inputType="text"
      _161
      android:lines="1"
      _161
      android:padding="5dp"
      _161
      app:layout_constraintStart_toStartOf="parent"
      _161
      app:layout_constraintTop_toTopOf="parent" />
      _161
      _161
      <EditText
      _161
      android:id="@+id/channel_name"
      _161
      android:layout_width="155dp"
      _161
      android:layout_height="41dp"
      _161
      android:layout_marginStart="36dp"
      _161
      android:layout_marginTop="124dp"
      _161
      android:layout_marginBottom="41dp"
      _161
      android:autofillHints=""
      _161
      android:ems="10"
      _161
      android:hint="@string/channel_name"
      _161
      android:inputType="text"
      _161
      app:layout_constraintBottom_toTopOf="@+id/msg_box"
      _161
      app:layout_constraintStart_toStartOf="parent"
      _161
      app:layout_constraintTop_toTopOf="parent" />
      _161
      _161
      <Button
      _161
      android:id="@+id/join_button"
      _161
      android:layout_width="wrap_content"
      _161
      android:layout_height="wrap_content"
      _161
      android:layout_marginStart="8dp"
      _161
      android:layout_marginTop="28dp"
      _161
      android:layout_marginBottom="24dp"
      _161
      android:onClick="onClickJoin"
      _161
      android:text="@string/join_button"
      _161
      app:layout_constraintBottom_toTopOf="@+id/msg_box"
      _161
      app:layout_constraintStart_toEndOf="@+id/channel_name"
      _161
      app:layout_constraintTop_toBottomOf="@+id/login_button" />
      _161
      _161
      <EditText
      _161
      android:id="@+id/msg_box"
      _161
      android:layout_width="198dp"
      _161
      android:layout_height="57dp"
      _161
      android:layout_marginStart="32dp"
      _161
      android:layout_marginTop="10dp"
      _161
      android:layout_marginBottom="12dp"
      _161
      android:autofillHints=""
      _161
      android:ems="10"
      _161
      android:hint="@string/msg"
      _161
      android:inputType="textPersonName"
      _161
      android:singleLine="false"
      _161
      app:layout_constraintBottom_toTopOf="@+id/peer_name"
      _161
      app:layout_constraintStart_toStartOf="parent"
      _161
      app:layout_constraintTop_toBottomOf="@+id/join_button" />
      _161
      _161
      <Button
      _161
      android:id="@+id/send_channel_msg_button"
      _161
      android:layout_width="126dp"
      _161
      android:layout_height="45dp"
      _161
      android:layout_marginTop="38dp"
      _161
      android:layout_marginEnd="36dp"
      _161
      android:layout_marginBottom="26dp"
      _161
      android:onClick="onClickSendChannelMsg"
      _161
      android:text="@string/send_channel_msg_button"
      _161
      app:layout_constraintBottom_toTopOf="@+id/send_peer_msg_button"
      _161
      app:layout_constraintEnd_toEndOf="parent"
      _161
      app:layout_constraintTop_toBottomOf="@+id/leave_button" />
      _161
      _161
      <Button
      _161
      android:id="@+id/logout_button"
      _161
      android:layout_width="wrap_content"
      _161
      android:layout_height="wrap_content"
      _161
      android:layout_marginStart="11dp"
      _161
      android:layout_marginTop="40dp"
      _161
      android:layout_marginEnd="28dp"
      _161
      android:onClick="onClickLogout"
      _161
      android:text="@string/logout_button"
      _161
      app:layout_constraintEnd_toEndOf="parent"
      _161
      app:layout_constraintHorizontal_bias="0.0"
      _161
      app:layout_constraintStart_toEndOf="@+id/login_button"
      _161
      app:layout_constraintTop_toTopOf="parent" />
      _161
      _161
      <Button
      _161
      android:id="@+id/leave_button"
      _161
      android:layout_width="wrap_content"
      _161
      android:layout_height="wrap_content"
      _161
      android:layout_marginTop="27dp"
      _161
      android:layout_marginEnd="28dp"
      _161
      android:onClick="onClickLeave"
      _161
      android:text="@string/leave_button"
      _161
      app:layout_constraintEnd_toEndOf="parent"
      _161
      app:layout_constraintTop_toBottomOf="@+id/logout_button" />
      _161
      _161
      <EditText
      _161
      android:id="@+id/peer_name"
      _161
      android:layout_width="121dp"
      _161
      android:layout_height="48dp"
      _161
      android:layout_marginStart="64dp"
      _161
      android:layout_marginTop="24dp"
      _161
      android:ems="10"
      _161
      android:hint="@string/peer_name"
      _161
      android:inputType="text"
      _161
      app:layout_constraintStart_toStartOf="parent"
      _161
      app:layout_constraintTop_toBottomOf="@+id/send_channel_msg_button" />
      _161
      _161
      <Button
      _161
      android:id="@+id/send_peer_msg_button"
      _161
      android:layout_width="wrap_content"
      _161
      android:layout_height="wrap_content"
      _161
      android:layout_marginTop="17dp"
      _161
      android:layout_marginEnd="56dp"
      _161
      android:onClick="onClickSendPeerMsg"
      _161
      android:text="@string/send_peer_msg_button"
      _161
      app:layout_constraintEnd_toEndOf="parent"
      _161
      app:layout_constraintTop_toBottomOf="@+id/send_channel_msg_button" />
      _161
      _161
      <TextView
      _161
      android:id="@+id/message_history"
      _161
      android:layout_width="412dp"
      _161
      android:layout_height="339dp"
      _161
      android:layout_marginTop="392dp"
      _161
      android:background="#AEA8A8"
      _161
      android:freezesText="false"
      _161
      android:isScrollContainer="false"
      _161
      android:scrollbars="vertical"
      _161
      android:textColor="#2196F3"
      _161
      app:layout_constraintBottom_toBottomOf="parent"
      _161
      app:layout_constraintEnd_toEndOf="parent"
      _161
      app:layout_constraintHorizontal_bias="0.491"
      _161
      app:layout_constraintStart_toStartOf="parent"
      _161
      app:layout_constraintTop_toTopOf="parent"
      _161
      app:layout_constraintVertical_bias="0.352" />
      _161
      _161
      _161
      </androidx.constraintlayout.widget.ConstraintLayout>

    2. Open the app/res/values/strings.xml file and replace the contents of the file with the following:


      _16
      <resources>
      _16
      <string name="app_name">RtmQuickstart</string>
      _16
      <string name="login_button">Login</string>
      _16
      <string name="logout_button">Logout</string>
      _16
      <string name="join_button">Join</string>
      _16
      <string name="leave_button">Leave</string>
      _16
      <string name="send_peer_msg_button">Peer MSG</string>
      _16
      <string name="send_channel_msg_button">Group MSG</string>
      _16
      <string name="uid">Enter your uid</string>
      _16
      <string name="msg">Enter your message</string>
      _16
      <string name="channel_name">Channel name</string>
      _16
      <string name="peer_name">Peer name</string>
      _16
      <string name="app_id">Your App ID</string>
      _16
      <string name="token">Your Token</string>
      _16
      _16
      </resources>

You need to edit the following fields:

  • Replace Your App ID with the App ID of your project.

  • Replace Your Token with the Token you obtained.

2. Implement the logic of sending and receiving messages

Open the app/java/com.example.rtmquickstart/MainActivity.java file and replace the contents of the file with the following:


_297
package com.example.rtmquickstart;
_297
_297
import androidx.appcompat.app.AppCompatActivity;
_297
_297
import android.os.Bundle;
_297
import android.view.View;
_297
import android.widget.EditText;
_297
import android.widget.TextView;
_297
import android.widget.Toast;
_297
_297
import java.util.List;
_297
import java.util.Map;
_297
_297
import io.agora.rtm.ErrorInfo;
_297
import io.agora.rtm.ResultCallback;
_297
import io.agora.rtm.RtmChannel;
_297
import io.agora.rtm.RtmChannelAttribute;
_297
import io.agora.rtm.RtmChannelListener;
_297
import io.agora.rtm.RtmChannelMember;
_297
import io.agora.rtm.RtmClient;
_297
import io.agora.rtm.RtmClientListener;
_297
import io.agora.rtm.RtmFileMessage;
_297
import io.agora.rtm.RtmImageMessage;
_297
import io.agora.rtm.RtmMediaOperationProgress;
_297
import io.agora.rtm.RtmMessage;
_297
import io.agora.rtm.SendMessageOptions;
_297
_297
public class MainActivity extends AppCompatActivity {
_297
_297
// Define global variables
_297
_297
// EditText objects from the UI
_297
private EditText et_uid;
_297
private EditText et_channel_name;
_297
private EditText et_message_content;
_297
private EditText et_peer_id;
_297
_297
// <Vg k="MESS" /> uid
_297
private String uid;
_297
// <Vg k="MESS" /> channel name
_297
private String channel_name;
_297
// Agora App ID
_297
private String AppID;
_297
_297
// <Vg k="MESS" /> client instance
_297
private RtmClient mRtmClient;
_297
// <Vg k="MESS" /> channel instance
_297
private RtmChannel mRtmChannel;
_297
_297
// TextView to show message records in the UI
_297
private TextView message_history;
_297
_297
// <Vg k="MESS" /> user ID of the message receiver
_297
private String peer_id;
_297
// Message content
_297
private String message_content;
_297
_297
@Override
_297
protected void onCreate(Bundle savedInstanceState) {
_297
super.onCreate(savedInstanceState);
_297
setContentView(R.layout.activity_main);
_297
_297
// Initialize the <Vg k="MESS" /> client
_297
try {
_297
AppID = getBaseContext().getString(R.string.app_id);
_297
// Initialize the <Vg k="MESS" /> client
_297
mRtmClient = RtmClient.createInstance(getBaseContext(), AppID,
_297
new RtmClientListener() {
_297
@Override
_297
public void onConnectionStateChanged(int state, int reason) {
_297
String text = "Connection state changed to " + state + "Reason: " + reason + "\n";
_297
writeToMessageHistory(text);
_297
}
_297
_297
@Override
_297
public void onImageMessageReceivedFromPeer(RtmImageMessage rtmImageMessage, String s) {
_297
}
_297
_297
@Override
_297
public void onFileMessageReceivedFromPeer(RtmFileMessage rtmFileMessage, String s) {
_297
}
_297
_297
@Override
_297
public void onMediaUploadingProgress(RtmMediaOperationProgress rtmMediaOperationProgress, long l) {
_297
}
_297
_297
@Override
_297
public void onMediaDownloadingProgress(RtmMediaOperationProgress rtmMediaOperationProgress, long l) {
_297
}
_297
_297
@Override
_297
public void onTokenExpired() {
_297
}
_297
_297
@Override
_297
public void onPeersOnlineStatusChanged(Map<String, Integer> map) {
_297
}
_297
_297
@Override
_297
public void onMessageReceived(RtmMessage rtmMessage, String peerId) {
_297
String text = "Message received from " + peerId + " Message: " + rtmMessage.getText() + "\n";
_297
writeToMessageHistory(text);
_297
}
_297
});
_297
_297
} catch (Exception e) {
_297
throw new RuntimeException("<Vg k="MESS" /> initialization failed!");
_297
}
_297
_297
}
_297
_297
// Button to login to Signaling
_297
public void onClickLogin(View v)
_297
{
_297
et_uid = (EditText) findViewById(R.id.uid);
_297
uid = et_uid.getText().toString();
_297
_297
String token =getBaseContext().getString(R.string.token);
_297
_297
// Log in to Signaling
_297
mRtmClient.login(token, uid, new ResultCallback<Void>() {
_297
@Override
_297
public void onSuccess(Void responseInfo) {
_297
}
_297
_297
@Override
_297
public void onFailure(ErrorInfo errorInfo) {
_297
CharSequence text = "User: " + uid + " failed to log in to Signaling!" + errorInfo.toString();
_297
int duration = Toast.LENGTH_SHORT;
_297
runOnUiThread(new Runnable() {
_297
public void run() {
_297
Toast toast = Toast.makeText(getApplicationContext(), text, duration);
_297
toast.show();
_297
}
_297
});
_297
_297
}
_297
});
_297
}
_297
_297
// Button to join the <Vg k="MESS" /> channel
_297
public void onClickJoin(View v)
_297
{
_297
et_channel_name = (EditText) findViewById(R.id.channel_name);
_297
channel_name = et_channel_name.getText().toString();
_297
// Create a channel listener
_297
RtmChannelListener mRtmChannelListener = new RtmChannelListener() {
_297
@Override
_297
public void onMemberCountUpdated(int i) {
_297
_297
}
_297
_297
@Override
_297
public void onAttributesUpdated(List<RtmChannelAttribute> list) {
_297
_297
}
_297
_297
@Override
_297
public void onMessageReceived(RtmMessage message, RtmChannelMember fromMember) {
_297
String text = message.getText();
_297
String fromUser = fromMember.getUserId();
_297
_297
String message_text = "Message received from " + fromUser + " : " + text + "\n";
_297
writeToMessageHistory(message_text);
_297
_297
}
_297
_297
@Override
_297
public void onImageMessageReceived(RtmImageMessage rtmImageMessage, RtmChannelMember rtmChannelMember) {
_297
_297
}
_297
_297
@Override
_297
public void onFileMessageReceived(RtmFileMessage rtmFileMessage, RtmChannelMember rtmChannelMember) {
_297
_297
}
_297
_297
@Override
_297
public void onMemberJoined(RtmChannelMember member) {
_297
_297
}
_297
_297
@Override
_297
public void onMemberLeft(RtmChannelMember member) {
_297
_297
}
_297
};
_297
_297
try {
_297
// Create an <Vg k="MESS" /> channel
_297
mRtmChannel = mRtmClient.createChannel(channel_name, mRtmChannelListener);
_297
} catch (RuntimeException e) {
_297
}
_297
// Join the <Vg k="MESS" /> channel
_297
mRtmChannel.join(new ResultCallback<Void>() {
_297
@Override
_297
public void onSuccess(Void responseInfo) {
_297
}
_297
_297
@Override
_297
public void onFailure(ErrorInfo errorInfo) {
_297
CharSequence text = "User: " + uid + " failed to join the channel!" + errorInfo.toString();
_297
int duration = Toast.LENGTH_SHORT;
_297
runOnUiThread(new Runnable() {
_297
public void run() {
_297
Toast toast = Toast.makeText(getApplicationContext(), text, duration);
_297
toast.show();
_297
}
_297
});
_297
_297
}
_297
});
_297
_297
}
_297
_297
// Button to log out of Signaling
_297
public void onClickLogout(View v)
_297
{
_297
// Log out of Signaling
_297
mRtmClient.logout(null);
_297
}
_297
_297
// Button to leave the <Vg k="MESS" /> channel
_297
public void onClickLeave(View v)
_297
{
_297
// Leave the <Vg k="MESS" /> channel
_297
mRtmChannel.leave(null);
_297
}
_297
// Button to send peer-to-peer message
_297
public void onClickSendPeerMsg(View v)
_297
{
_297
et_message_content = findViewById(R.id.msg_box);
_297
message_content = et_message_content.getText().toString();
_297
_297
et_peer_id = findViewById(R.id.peer_name);
_297
peer_id = et_peer_id.getText().toString();
_297
_297
// Create <Vg k="MESS" /> message instance
_297
final RtmMessage message = mRtmClient.createMessage();
_297
message.setText(message_content);
_297
_297
SendMessageOptions option = new SendMessageOptions();
_297
option.enableOfflineMessaging = true;
_297
_297
// Send message to peer
_297
mRtmClient.sendMessageToPeer(peer_id, message, option, new ResultCallback<Void>() {
_297
_297
@Override
_297
public void onSuccess(Void aVoid) {
_297
String text = "Message sent from " + uid + " To " + peer_id + " : " + message.getText() + "\n";
_297
writeToMessageHistory(text);
_297
}
_297
_297
@Override
_297
public void onFailure(ErrorInfo errorInfo) {
_297
String text = "Message fails to send from " + uid + " To " + peer_id + " Error : " + errorInfo + "\n";
_297
writeToMessageHistory(text);
_297
_297
}
_297
});
_297
_297
}
_297
// Button to send channel message
_297
public void onClickSendChannelMsg(View v)
_297
{
_297
et_message_content = findViewById(R.id.msg_box);
_297
message_content = et_message_content.getText().toString();
_297
_297
// Create <Vg k="MESS" /> message instance
_297
RtmMessage message = mRtmClient.createMessage();
_297
message.setText(message_content);
_297
_297
// Send message to channel
_297
mRtmChannel.sendMessage(message, new ResultCallback<Void>() {
_297
@Override
_297
public void onSuccess(Void aVoid) {
_297
String text = "Message sent to channel " + mRtmChannel.getId() + " : " + message.getText() + "\n";
_297
writeToMessageHistory(text);
_297
}
_297
_297
@Override
_297
public void onFailure(ErrorInfo errorInfo) {
_297
String text = "Message fails to send to channel " + mRtmChannel.getId() + " Error: " + errorInfo + "\n";
_297
writeToMessageHistory(text);
_297
}
_297
});
_297
_297
}
_297
_297
// Write message records to the TextView
_297
public void writeToMessageHistory(String record)
_297
{
_297
message_history = findViewById(R.id.message_history);
_297
message_history.append(record);
_297
}
_297
_297
}

Test your app

Build the project in Android Studio, and run it on a simulator or a physical mobile device. If the project runs successfully, you are able to:

  • Log in to and log out of Agora Signaling.

  • Join and leave a channel.

  • Send and receive peer-to-peer or channel messages.

  • See the changes in the connection state of the user and changes in the channel state.

You can see the following page if your project runs successfully:

1623051651101

Considerations

  • The Agora Signaling SDK supports creating multiple RtmClient instances that are independent of each other.

  • To send and receive peer-to-peer or channel messages, ensure that you have successfully logged in the Agora Signaling (you have received onSuccessonSuccess.

  • To use any of the channel features, you must first call the createChannel method to create a channel instance.

  • You can create multiple channel instances for each RtmClient instance, but you can only join a maximum of 20 channels at the same time. The channelId parameter needs to be channel-specific.

  • When you leave a channel and do not want to join it again, you can call the release method to release all resources used by the channel instance.

  • You cannot reuse a received RtmMessage instance.

Next steps

Generating a token by hand is not helpful in a production context. Authenticate Your Users with Tokens shows you how to start Signaling with a token that you retrieve from your server.

See also

Sample project

Agora also provides an open-source [sample project](https://github.com/AgoraIO/Signaling/tree/master/Agora-Signaling-Tutorial-Android) on GitHub for your reference.

Other approaches to integrate the SDK

Downloads shows you alternative ways to add Signaling SDK in your project.

Next steps

Generating a token by hand is not helpful in a production context. Authenticate Your Users with Tokens shows you how to start live streaming with a token that you retrieve from your server.

Page Content