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

1624338956334

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

1624357960276

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

1624338963311

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.

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

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:

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

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

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

4. Integrate the SDK

Integrate the Agora RTM 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:

    buildscript {
    repositories {

    mavenCentral()
    }

    }

    allprojects {
    repositories {

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


    dependencies {

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

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:

      <?xml version="1.0" encoding="utf-8"?>
      <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:background="#FFFFFF"
      tools:context=".MainActivity">

      <Button
      android:id="@+id/login_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginStart="4dp"
      android:layout_marginTop="40dp"
      android:layout_marginEnd="132dp"
      android:onClick="onClickLogin"
      android:text="@string/login_button"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.0"
      app:layout_constraintStart_toEndOf="@+id/uid"
      app:layout_constraintTop_toTopOf="parent" />

      <EditText
      android:id="@+id/uid"
      android:layout_width="150dp"
      android:layout_height="40dp"
      android:layout_marginStart="37dp"
      android:layout_marginTop="40dp"
      android:autofillHints=""
      android:hint="@string/uid"
      android:inputType="text"
      android:lines="1"
      android:padding="5dp"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent" />

      <EditText
      android:id="@+id/channel_name"
      android:layout_width="155dp"
      android:layout_height="41dp"
      android:layout_marginStart="36dp"
      android:layout_marginTop="124dp"
      android:layout_marginBottom="41dp"
      android:autofillHints=""
      android:ems="10"
      android:hint="@string/channel_name"
      android:inputType="text"
      app:layout_constraintBottom_toTopOf="@+id/msg_box"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent" />

      <Button
      android:id="@+id/join_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginStart="8dp"
      android:layout_marginTop="28dp"
      android:layout_marginBottom="24dp"
      android:onClick="onClickJoin"
      android:text="@string/join_button"
      app:layout_constraintBottom_toTopOf="@+id/msg_box"
      app:layout_constraintStart_toEndOf="@+id/channel_name"
      app:layout_constraintTop_toBottomOf="@+id/login_button" />

      <EditText
      android:id="@+id/msg_box"
      android:layout_width="198dp"
      android:layout_height="57dp"
      android:layout_marginStart="32dp"
      android:layout_marginTop="10dp"
      android:layout_marginBottom="12dp"
      android:autofillHints=""
      android:ems="10"
      android:hint="@string/msg"
      android:inputType="textPersonName"
      android:singleLine="false"
      app:layout_constraintBottom_toTopOf="@+id/peer_name"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/join_button" />

      <Button
      android:id="@+id/send_channel_msg_button"
      android:layout_width="126dp"
      android:layout_height="45dp"
      android:layout_marginTop="38dp"
      android:layout_marginEnd="36dp"
      android:layout_marginBottom="26dp"
      android:onClick="onClickSendChannelMsg"
      android:text="@string/send_channel_msg_button"
      app:layout_constraintBottom_toTopOf="@+id/send_peer_msg_button"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/leave_button" />

      <Button
      android:id="@+id/logout_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginStart="11dp"
      android:layout_marginTop="40dp"
      android:layout_marginEnd="28dp"
      android:onClick="onClickLogout"
      android:text="@string/logout_button"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.0"
      app:layout_constraintStart_toEndOf="@+id/login_button"
      app:layout_constraintTop_toTopOf="parent" />

      <Button
      android:id="@+id/leave_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="27dp"
      android:layout_marginEnd="28dp"
      android:onClick="onClickLeave"
      android:text="@string/leave_button"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/logout_button" />

      <EditText
      android:id="@+id/peer_name"
      android:layout_width="121dp"
      android:layout_height="48dp"
      android:layout_marginStart="64dp"
      android:layout_marginTop="24dp"
      android:ems="10"
      android:hint="@string/peer_name"
      android:inputType="text"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/send_channel_msg_button" />

      <Button
      android:id="@+id/send_peer_msg_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="17dp"
      android:layout_marginEnd="56dp"
      android:onClick="onClickSendPeerMsg"
      android:text="@string/send_peer_msg_button"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/send_channel_msg_button" />

      <TextView
      android:id="@+id/message_history"
      android:layout_width="412dp"
      android:layout_height="339dp"
      android:layout_marginTop="392dp"
      android:background="#AEA8A8"
      android:freezesText="false"
      android:isScrollContainer="false"
      android:scrollbars="vertical"
      android:textColor="#2196F3"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.491"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintVertical_bias="0.352" />


      </androidx.constraintlayout.widget.ConstraintLayout>
      Copy
    2. Open the app/res/values/strings.xml file and replace the contents of the file with the following:

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

      </resources>
      Copy

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:

package com.example.rtmquickstart;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;
import java.util.Map;

import io.agora.rtm.ErrorInfo;
import io.agora.rtm.ResultCallback;
import io.agora.rtm.RtmChannel;
import io.agora.rtm.RtmChannelAttribute;
import io.agora.rtm.RtmChannelListener;
import io.agora.rtm.RtmChannelMember;
import io.agora.rtm.RtmClient;
import io.agora.rtm.RtmClientListener;
import io.agora.rtm.RtmFileMessage;
import io.agora.rtm.RtmImageMessage;
import io.agora.rtm.RtmMediaOperationProgress;
import io.agora.rtm.RtmMessage;
import io.agora.rtm.SendMessageOptions;

public class MainActivity extends AppCompatActivity {

// Define global variables

// EditText objects from the UI
private EditText et_uid;
private EditText et_channel_name;
private EditText et_message_content;
private EditText et_peer_id;

// RTM uid
private String uid;
// RTM channel name
private String channel_name;
// Agora App ID
private String AppID;

// RTM client instance
private RtmClient mRtmClient;
// RTM channel instance
private RtmChannel mRtmChannel;

// TextView to show message records in the UI
private TextView message_history;

// RTM user ID of the message receiver
private String peer_id;
// Message content
private String message_content;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Initialize the RTM client
try {
AppID = getBaseContext().getString(R.string.app_id);
// Initialize the RTM client
mRtmClient = RtmClient.createInstance(getBaseContext(), AppID,
new RtmClientListener() {
@Override
public void onConnectionStateChanged(int state, int reason) {
String text = "Connection state changed to " + state + "Reason: " + reason + "\n";
writeToMessageHistory(text);
}

@Override
public void onImageMessageReceivedFromPeer(RtmImageMessage rtmImageMessage, String s) {
}

@Override
public void onFileMessageReceivedFromPeer(RtmFileMessage rtmFileMessage, String s) {
}

@Override
public void onMediaUploadingProgress(RtmMediaOperationProgress rtmMediaOperationProgress, long l) {
}

@Override
public void onMediaDownloadingProgress(RtmMediaOperationProgress rtmMediaOperationProgress, long l) {
}

@Override
public void onTokenExpired() {
}

@Override
public void onPeersOnlineStatusChanged(Map<String, Integer> map) {
}

@Override
public void onMessageReceived(RtmMessage rtmMessage, String peerId) {
String text = "Message received from " + peerId + " Message: " + rtmMessage.getText() + "\n";
writeToMessageHistory(text);
}
});

} catch (Exception e) {
throw new RuntimeException("RTM initialization failed!");
}

}

// Button to login to Signaling
public void onClickLogin(View v)
{
et_uid = (EditText) findViewById(R.id.uid);
uid = et_uid.getText().toString();

String token =getBaseContext().getString(R.string.token);

// Log in to Signaling
mRtmClient.login(token, uid, new ResultCallback<Void>() {
@Override
public void onSuccess(Void responseInfo) {
}

@Override
public void onFailure(ErrorInfo errorInfo) {
CharSequence text = "User: " + uid + " failed to log in to Signaling!" + errorInfo.toString();
int duration = Toast.LENGTH_SHORT;
runOnUiThread(new Runnable() {
public void run() {
Toast toast = Toast.makeText(getApplicationContext(), text, duration);
toast.show();
}
});

}
});
}

// Button to join the RTM channel
public void onClickJoin(View v)
{
et_channel_name = (EditText) findViewById(R.id.channel_name);
channel_name = et_channel_name.getText().toString();
// Create a channel listener
RtmChannelListener mRtmChannelListener = new RtmChannelListener() {
@Override
public void onMemberCountUpdated(int i) {

}

@Override
public void onAttributesUpdated(List<RtmChannelAttribute> list) {

}

@Override
public void onMessageReceived(RtmMessage message, RtmChannelMember fromMember) {
String text = message.getText();
String fromUser = fromMember.getUserId();

String message_text = "Message received from " + fromUser + " : " + text + "\n";
writeToMessageHistory(message_text);

}

@Override
public void onImageMessageReceived(RtmImageMessage rtmImageMessage, RtmChannelMember rtmChannelMember) {

}

@Override
public void onFileMessageReceived(RtmFileMessage rtmFileMessage, RtmChannelMember rtmChannelMember) {

}

@Override
public void onMemberJoined(RtmChannelMember member) {

}

@Override
public void onMemberLeft(RtmChannelMember member) {

}
};

try {
// Create an RTM channel
mRtmChannel = mRtmClient.createChannel(channel_name, mRtmChannelListener);
} catch (RuntimeException e) {
}
// Join the RTM channel
mRtmChannel.join(new ResultCallback<Void>() {
@Override
public void onSuccess(Void responseInfo) {
}

@Override
public void onFailure(ErrorInfo errorInfo) {
CharSequence text = "User: " + uid + " failed to join the channel!" + errorInfo.toString();
int duration = Toast.LENGTH_SHORT;
runOnUiThread(new Runnable() {
public void run() {
Toast toast = Toast.makeText(getApplicationContext(), text, duration);
toast.show();
}
});

}
});

}

// Button to log out of Signaling
public void onClickLogout(View v)
{
// Log out of Signaling
mRtmClient.logout(null);
}

// Button to leave the RTM channel
public void onClickLeave(View v)
{
// Leave the RTM channel
mRtmChannel.leave(null);
}
// Button to send peer-to-peer message
public void onClickSendPeerMsg(View v)
{
et_message_content = findViewById(R.id.msg_box);
message_content = et_message_content.getText().toString();

et_peer_id = findViewById(R.id.peer_name);
peer_id = et_peer_id.getText().toString();

// Create RTM message instance
final RtmMessage message = mRtmClient.createMessage();
message.setText(message_content);

SendMessageOptions option = new SendMessageOptions();
option.enableOfflineMessaging = true;

// Send message to peer
mRtmClient.sendMessageToPeer(peer_id, message, option, new ResultCallback<Void>() {

@Override
public void onSuccess(Void aVoid) {
String text = "Message sent from " + uid + " To " + peer_id + "" + message.getText() + "\n";
writeToMessageHistory(text);
}

@Override
public void onFailure(ErrorInfo errorInfo) {
String text = "Message fails to send from " + uid + " To " + peer_id + " Error : " + errorInfo + "\n";
writeToMessageHistory(text);

}
});

}
// Button to send channel message
public void onClickSendChannelMsg(View v)
{
et_message_content = findViewById(R.id.msg_box);
message_content = et_message_content.getText().toString();

// Create RTM message instance
RtmMessage message = mRtmClient.createMessage();
message.setText(message_content);

// Send message to channel
mRtmChannel.sendMessage(message, new ResultCallback<Void>() {
@Override
public void onSuccess(Void aVoid) {
String text = "Message sent to channel " + mRtmChannel.getId() + " : " + message.getText() + "\n";
writeToMessageHistory(text);
}

@Override
public void onFailure(ErrorInfo errorInfo) {
String text = "Message fails to send to channel " + mRtmChannel.getId() + " Error: " + errorInfo + "\n";
writeToMessageHistory(text);
}
});

}

// Write message records to the TextView
public void writeToMessageHistory(String record)
{
message_history = findViewById(R.id.message_history);
message_history.append(record);
}

}
Copy

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