This article describes how to create a basic project and use the Agora RTM SDK to send and receive messages.
The login process includes:
The peer messaging process includes:
The channel messaging process includes:
For an app client to join a channel, you need the following information:
Follow the steps to create the environment necessary to add real-time messaging into your app.
Use Android Studio to create an Android project.
RtmQuickstart
as the Name.com.example.rtmquickstart
as the Package name. 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" />
Add the following line in the app/proguard-rules.pro
file to prevent code obfuscation:
-keep class io.agora.**{*;}
If an error is reported, you can change the code as:
-keep class io.agora**{*;}
Integrate the Agora RTM Android SDK into your project with Maven Central. For more integration methods, see Other approaches to integrate the SDK.
a. In /Gradle Scripts/build.gradle(Project: <projectname>)
, add the following lines to add the Maven Central dependency:
buildscript {
repositories {
...
mavenCentral()
}
...
}
allprojects {
repositories {
...
mavenCentral()
}
}
b. 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.
// For RTM SDK for Android v1.4.5, use implementation 'io.agora.rtm:rtm-sdk:1.4.5.0' instead.
implementation 'io.agora.rtm:rtm-sdk:x.y.z'
}
This section shows how to use the Agora RTM SDK to implement real-time messaging into your app step by step.
To help you quickly understand and implement functions of Agora RTM system, this section shows how to implement the following functions in an Activity with minimum effort:
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>
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>
You need to edit the following fields:
Your App ID
with the App ID of your project from the Agora console. See Get the App ID for details.Your Token
with the Token you obtained from the Agora token-builder. See Generate an RTM token for details.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 the RTM system
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 the RTM system
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 the RTM system!" + 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 the RTM system
public void onClickLogout(View v)
{
// Log out of the RTM system
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);
}
}
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:
You can see the following page if your project runs successfully:
The Agora RTM 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 RTM system (you have received onSuccess).
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.
Generating a token by hand is not helpful in a production context. Authenticate Your Users with Tokens shows you how to start real-time messaging with a token that you retrieve from your server.
Agora also provides an open-source sample project on GitHub for your reference.
In addition to integrating the Agora RTM SDK for Android through MavenCentral, you can also import the SDK into your project through JitPack or by manually copying the SDK files.
Add the address of Jitpack in the build.gradle
file under the root directory of your project.
...
allprojects {
repositories {
...
maven { url 'https://www.jitpack.io' }
}
}
...
Add the com.github.agorabuilder:rtm-sdk
dependency in the /app/build.gradle
file under your project (for X.Y.Z, please fill in the current SDK version number). You can visit JitPack.io to see the latest version number.
dependencies: {
...
implementation 'com.github.agorabuilder:rtm-sdk:X.Y.Z'
}
File | Project Folder |
---|---|
agora-rtm_sdk.jar | ~/app/libs/ |
/arm64-v8a/libagora-rtm-sdk-jni.so | ~/app/src/main/jniLibs/arm64-v8a/ |
/armeabi-v7a/libagora-rtm-sdk-jni.so | ~/app/src/main/jniLibs/armeabi-v7a/ |
/x86/libagora-rtm-jni.so | ~/app/src/main/jniLibs/x86/ |
/x86_64/libagora-rtm-sdk-jni.so | ~/app/src/main/jniLibs/x86_64/ |