Instant messaging connects people wherever they are and allows them to communicate with others in real time. The Agora Chat SDK enables you to embed real-time messaging in any app, on any device, anywhere.
This page shows a sample code to add peer-to-peer messaging into your app by using the Agora Chat SDK for Android.
The following figure shows the workflow of how clients send and receive peer-to-peer messages.
As shown in the figure, the workflow of peer-to-peer messaging is as follows:
In order to follow the procedure in this page, you must have:
Follow the steps to create the environment necessary to integrate Agora Chat into your app.
For new projects, in Android Studio, create a Phone and Tablet Android project with an Empty Activity.
Integrate the Agora Chat SDK into your project with Maven Central.
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 Chat SDK into your Android project:
android {
defaultConfig {
// The Android OS version should be 21 or higher.
minSdkVersion 21
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
...
implementation 'io.agora.rtc:chat-sdk:X.Y.Z'
}
minSdkVersion
must be 21 or higher for the build process to succeed.Add permissions for network and device access.
In /app/Manifests/AndroidManifest.xml
, add the following permissions after </application>
:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!—- Need to add after Android 12, apply for alarm clock timing permission -—>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
These are the minimum permissions you need to add to start Agora Chat. You can also add other permissions according to your use case.
Prevent code obfuscation.
In /Gradle Scripts/proguard-rules.pro
, add the following line:
-keep class io.agora.** {*;}
-dontwarn io.agora.**
This section shows how to use the Agora Chat SDK to implement peer-to-peer messaging in your app step by step.
To add the text strings used by the UI, open app/res/values/strings.xml
and replace the content with the following codes:
<resources>
<string name="app_name">AgoraChatQuickstart</string>
<string name="app_key">41117440#383391</string>
<string name="base_url"><#Developer Token Server#></string>
<string name="login_url">%1$s/app/chat/user/login</string>
<string name="register_url">%1$s/app/chat/user/register</string>
</resources>
When fetching a token, your token server may differ slightly from our example backend service logic.
To make this step easier to test, use the temporary token server "https://a41.chat.agora.io" provided by Agora in the placeholder above(<#Developer Token Server#>). When you're ready to deploy your own server, swap out your server's URL there, and update any of the POST request logic along with that.
To add the UI framework, open app/res/layout/activity_main.xml
and replace the content with the following codes:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="io.agora.agorachatquickstart.MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp">
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter username"
android:layout_marginTop="20dp"/>
<EditText
android:id="@+id/et_pwd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter password"
android:inputType="textPassword"
android:layout_marginTop="10dp"/>
<Button
android:id="@+id/btn_sign_in"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign in"
android:onClick="signInWithToken"
android:layout_marginTop="10dp"/>
<Button
android:id="@+id/btn_sign_out"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign out"
android:onClick="signOut"
android:layout_marginTop="10dp"/>
<Button
android:id="@+id/btn_sign_up"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sign up"
android:onClick="signUp"
android:layout_marginTop="10dp"/>
<EditText
android:id="@+id/et_to_chat_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter another username"
android:layout_marginTop="20dp"/>
<EditText
android:id="@+id/et_msg_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter content"
android:layout_marginTop="10dp"/>
<Button
android:id="@+id/btn_send_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send message"
android:onClick="sendFirstMessage"
android:layout_marginTop="20dp"/>
</LinearLayout>
</ScrollView>
<TextView
android:id="@+id/tv_log"
android:layout_width="match_parent"
android:layout_height="200dp"
android:hint="Show log area..."
android:scrollbars="vertical"
android:padding="10dp"/>
</LinearLayout>
To enable your app to send and receive messages between individual users, do the following:
Import classes. In app/java/io.agora.agorachatquickstart/MainActivity
, add the following lines after import android.os.Bundle;
:
import static io.agora.cloud.HttpClientManager.Method_POST;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import io.agora.CallBack;
import io.agora.ConnectionListener;
import io.agora.Error;
import io.agora.ValueCallBack;
import io.agora.chat.ChatClient;
import io.agora.chat.ChatMessage;
import io.agora.chat.ChatOptions;
import io.agora.chat.TextMessageBody;
import io.agora.cloud.HttpClientManager;
import io.agora.cloud.HttpResponse;
Define global variables. In app/java/io.agora.agorachatquickstart/MainActivity
, before adding the following lines after AppCompatActivity {
, ensure you delete the onCreate
function created by default.
private EditText et_username;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initSDK();
initListener();
}
Initialize the view and the app. In app/java/io.agora.agorachatquickstart/MainActivity
, add the following lines after the onCreate
function:
// Initializes the view.
private void initView() {
et_username = findViewById(R.id.et_username);
((TextView)findViewById(R.id.tv_log)).setMovementMethod(new ScrollingMovementMethod());
}
// Initializes the SDK.
private void initSDK() {
ChatOptions options = new ChatOptions();
// Sets your app key applied via Agora Console.
String sdkAppkey = getString(R.string.app_key);
if(TextUtils.isEmpty(sdkAppkey)) {
Toast.makeText(MainActivity.this, "You should set your AppKey first!", Toast.LENGTH_SHORT).show();
return;
}
// Sets your app key to options.
options.setAppKey(sdkAppkey);
// Initializes the Agora Chat SDK.
ChatClient.getInstance().init(this, options);
// Makes the Agora Chat SDK debuggable.
ChatClient.getInstance().setDebugMode(true);
}
Retrieve a token. To get a token from the app server, add the following lines after the initSDK
function:
private void getTokenFromAppServer(boolean isRenewToken) {
if(ChatClient.getInstance().isLoggedInBefore()) {
showLog("An account has been signed in, please sign out first and then sign in", false);
return;
}
String username = et_username.getText().toString().trim();
String pwd = ((EditText) findViewById(R.id.et_pwd)).getText().toString().trim();
getAgoraTokenFromAppServer(username, pwd, new ValueCallBack<String>() {
@Override
public void onSuccess(String token) {
if(isRenewToken) {
ChatClient.getInstance().renewToken(token);
}else {
login(username,token);
}
}
@Override
public void onError(int error, String errorMsg) {
showLog(errorMsg, true);
}
});
}
// Retrieves a token from the app server.
private void getAgoraTokenFromAppServer(String username, String pwd, @NonNull ValueCallBack<String> callBack) {
showLog("begin to getTokenFromAppServer ...", false);
executeRequest(getString(R.string.login_url, getString(R.string.base_url)), username, pwd, new ValueCallBack<String>() {
@Override
public void onSuccess(String response) {
try {
JSONObject object = new JSONObject(response);
String token = object.getString("accessToken");
callBack.onSuccess(token);
} catch (JSONException e) {
callBack.onError(Error.GENERAL_ERROR, e.getMessage());
}
}
@Override
public void onError(int error, String errorMsg) {
callBack.onError(error, errorMsg);
}
});
}
private void executeRequest(String url, String username, String password, @NonNull ValueCallBack<String> callBack) {
if(TextUtils.isEmpty(url) || TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
callBack.onError(Error.INVALID_PARAM, "Request url, username or password should not be empty");
return;
}
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
JSONObject request = new JSONObject();
try {
request.putOpt("userAccount", username);
request.putOpt("userPassword", password);
} catch (JSONException e) {
callBack.onError(Error.GENERAL_ERROR, e.getMessage());
return;
}
execute(()-> {
try {
HttpResponse response = HttpClientManager.httpExecute(url, headers, request.toString(), Method_POST);
int code = response.code;
String responseInfo = response.content;
if (code == 200) {
if (responseInfo != null && responseInfo.length() > 0) {
callBack.onSuccess(responseInfo);
} else {
callBack.onError(Error.SERVER_UNKNOWN_ERROR, responseInfo);
}
} else {
callBack.onError(code, responseInfo);
}
} catch (Exception e) {
callBack.onError(Error.GENERAL_ERROR, e.getMessage());
}
});
}
private void execute(Runnable runnable) {
new Thread(runnable).start();
}
Add event callbacks. In app/java/io.agora.agorachatquickstart/MainActivity
, add the following lines after the execute
function:
private void initListener() {
// Add message events callbacks.
ChatClient.getInstance().chatManager().addMessageListener(messages -> {
for(ChatMessage message : messages) {
StringBuilder builder = new StringBuilder();
builder.append("Receive a ").append(message.getType().name())
.append(" message from: ").append(message.getFrom());
if(message.getType() == ChatMessage.Type.TXT) {
builder.append(" content:")
.append(((TextMessageBody)message.getBody()).getMessage());
}
showLog(builder.toString(), false);
}
});
// Add connection events callbacks.
ChatClient.getInstance().addConnectionListener(new ConnectionListener() {
@Override
public void onConnected() {
showLog("onConnected",false);
}
@Override
public void onDisconnected(int error) {
showLog("onDisconnected: "+error,false);
}
@Override
public void onLogout(int errorCode) {
showLog("User needs to log out: "+errorCode, false);
ChatClient.getInstance().logout(false, null);
}
// This callback occurs when the token expires. When the callback is triggered, the app client must get a new token from the app server and logs in to the app again.
@Override
public void onTokenExpired() {
showLog("ConnectionListener onTokenExpired", true);
signInWithToken(null);
}
// This callback occurs when the token is to expire.
@Override
public void onTokenWillExpire() {
showLog("ConnectionListener onTokenWillExpire", true);
getTokenFromAppServer(true);
}
});
}
Create a user account and log in to the app. To implement this logic, in app/java/io.agora.agorachatquickstart/MainActivity
, add the following lines after the initListener
function:
// Signs up with a username and a password.
public void signUp(View view) {
String username = et_username.getText().toString().trim();
String pwd = ((EditText) findViewById(R.id.et_pwd)).getText().toString().trim();
register(username, pwd, new CallBack() {
@Override
public void onSuccess() {
showLog("Sign up success!", true);
}
@Override
public void onError(int code, String error) {
showLog(error, true);
}
});
}
// Logs in with the token.
public void signInWithToken(View view) {
getTokenFromAppServer(false);
}
// Signs out.
public void signOut(View view) {
if(ChatClient.getInstance().isLoggedInBefore()) {
ChatClient.getInstance().logout(true, new CallBack() {
@Override
public void onSuccess() {
showLog("Sign out success!", true);
}
@Override
public void onError(int code, String error) {
showLog(error, true);
}
});
}
}
private void register(String username, String pwd, @NonNull CallBack callBack) {
showLog("begin to sign up...",false);
executeRequest(getString(R.string.register_url, getString(R.string.base_url)), username, pwd, new ValueCallBack<String>() {
@Override
public void onSuccess(String response) {
String resultCode = null;
try {
JSONObject object = new JSONObject(response);
resultCode = object.getString("code");
if(resultCode.equals("RES_OK")) {
callBack.onSuccess();
}else{
callBack.onError(Error.GENERAL_ERROR, "Sign up failed!");
}
} catch (JSONException e) {
callBack.onError(Error.GENERAL_ERROR, e.getMessage());
}
}
@Override
public void onError(int error, String errorMsg) {
callBack.onError(error, errorMsg);
}
});
}
Start a chat. To enable the function of sending messages, add the following lines after the register
function:
// Sends your first message.
public void sendFirstMessage(View view) {
String toSendName = ((TextView)findViewById(R.id.et_to_chat_name)).getText().toString().trim();
String content = ((TextView)findViewById(R.id.et_msg_content)).getText().toString().trim();
// Creates a text message.
ChatMessage message = ChatMessage.createTextSendMessage(content, toSendName);
// Sets the message callback before sending the message.
message.setMessageStatusCallback(new CallBack() {
@Override
public void onSuccess() {
showLog("Send message success!", true);
}
@Override
public void onError(int code, String error) {
showLog(error, true);
}
});
// Sends the message.
ChatClient.getInstance().chatManager().sendMessage(message);
}
Click Sync Project with Gradle Files
to sync your project. Now you are ready to test your app.
To validate the peer-to-peer messaging you have just integrated into your app using Agora Chat:
In Android Studio, click Run 'app'
.
You see the following interface on your simulator or physical device:
Create a user account and click SIGN UP.
Sign in with the user account you just created and send a message.
Run the app on another Android device or simulator and create another user account. Ensure that the usernames you created are unique.
Send messages between the users.
For demonstration purposes, Agora Chat provides an app server that enables you to quickly retrieve a token using the App Key given in this guide. In a production context, the best practice is for you to deploy your own token server, use your own App Key to generate a token, and retrieve the token on the client side to log in to Agora. To see how to implement a server that generates and serves tokens on request, see Generate a User Token.
In addition to integrating the Agora Chat SDK into your project with mavenCentral, you can also manually download the Agora Chat SDK for Android.
Download the latest version of the Agora Chat SDK for Android, and extract the files from the downloaded SDK package.
Copy the following files or subfolders from the libs folder of the downloaded SDK to the corresponding directory of your project.
File or subfolder | Path of your project |
---|---|
agorachat_X.Y.Z.jar |
~/app/libs/ |
/arm64-v8a/libagora-chat-sdk.so and libsqlite.so |
~/app/src/main/jniLibs/arm64-v8a/ |
/armeabi-v7a/libagora-chat-sdk.so and libsqlite.so |
~/app/src/main/jniLibs/armeabi-v7a/ |
/x86/libagora-chat-sdk.so and libsqlite.so |
~/app/src/main/jniLibs/x86/ |
/x86_64/libagora-chat-sdk.so and libsqlite.so |
~/app/src/main/jniLibs/x86_64/ |
For details, see the sample code for getting started with Agora Chat.