Messengers remain one of the most popular applications on Android โaccording to Statista, in 2026, more than 80% of smartphone users use at least one chat client. Creating your own analogue WhatsApp can become both a training project for beginning developers and the basis for a commercial startup. However, it is important to understand: copying the functionality of popular services requires compliance with laws on the protection of personal data (in the Russian Federation - 152-FZ, in the EU - GDPR)as well as publishing rules in Google Play.
In this guide we will analyze technical side developments: from choosing an architecture to push notification integration. You will learn how to organize real-time messaging, encrypt data, and optimize the application on weak devices. At the same time, we will not touch upon the issues of monetization or marketing - we will focus exclusively on Android-development and backend.
To successfully complete the project you will need:
- ๐ฑ Android Studio (version
Giraffe 2022.3.1or newer) - ๐ฅ Firebase (for authentication and cloud functions)
- ๐ป Knowledge
Kotlin/Javaand basicsREST API - ๐ Stable Internet connection for testing notifications
1. Choosing a technology stack: what you need for a messenger
The first step is to decide on the tools. Classic WhatsApp uses Erlang for the server part and protocol XMPP (with modifications), but for an educational project this is redundant. We will offer a simplified stack that will allow you to deploy a working prototype in 2-3 weeks:
| Component | Recommended technologies | Alternatives |
|---|---|---|
| Client (Android) | Kotlin + Jetpack Compose |
Java + XML-markup |
| Server/backend | Firebase Realtime Database + Cloud Functions | Node.js + Socket.io |
| Authentication | Firebase Authentication (by phone/email) | OAuth 2.0 (via Google Sign-In) |
| Notifications | Firebase Cloud Messaging (FCM) | OneSignal or Pushy |
| Media storage | Firebase Storage | AWS S3 or Backblaze B2 |
โ ๏ธ Attention: If you plan to scale the project to thousands of users, Firebase it may be expensive - tariffs depend on the number of read/write operations. For testing, the free plan is enough (Spark Plan), but for production, consider self-hosting at VPS (for example, DigitalOcean or Hetzner).
Enough to get started - it provides ready-made solutions for authentication, database and notifications. However, if you need complete independence from third-party services, you will have to deploy your backend on Firebase - It provides ready-made solutions for authentication, database and notifications. However, if you need complete independence from third-party services, you will have to deploy your backend on Node.js/Python using WebSocket for real-time chat.
2. Setting up Firebase: registering a project and connecting the SDK
Go to the website Firebase Console and create a new project. In the Project settings section, add Android application, specifying the package name (for example, com.yourcompany.messenger). After registration, download the file google-services.json โit will be needed for integration with Android Studio.
In the root file build.gradle of your project, add the dependency:
dependencies {classpath 'com.google.gms:google-services:4.4.1' // Check the current version
}
Then in the file build.gradle module (app) add:
apply plugin: 'com.google.gms.google-services'dependencies {
implementation 'com.google.firebase:firebase-bom:32.7.2'
implementation 'com.google.firebase:firebase-auth-ktx'
implementation 'com.google.firebase:firebase-database-ktx'
implementation 'com.google.firebase:firebase-storage-ktx'
implementation 'com.google.firebase:firebase-messaging-ktx'
}
โ ๏ธ Attention: Library versions may change. Always check the current numbers in Firebase official documentation. After adding dependencies, sync the project with Gradle.
Now connect google-services.json:
- Copy the file to
app/your project folder. - Make sure that
settings.gradleis written:
dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
โ๏ธ Preparing Firebase for work
3. Implementation of user authentication
Secure authentication is the basis of any messenger. We will use Firebase Authentication with entry by phone number (as in WhatsApp). To do this:
- B Firebase Console go to section
- Activate the method
Phone. - For testing, add your number to
Phone numbers for testing(for example,+79123456789,123456, where123456is a test confirmation code).
Authentication โ Sign-in method.
Initialize in the code Firebase Auth and configure sending SMS:
// In your Activity/Fragmentprivate lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
auth = Firebase.auth
// Example of sending a code to a number
val phoneNumber = "+79123456789"
val options = PhoneAuthOptions.newBuilder(auth)
.setPhoneNumber(phoneNumber)
.setTimeout(60L, TimeUnit.SECONDS)
.setActivity(this)
.setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
// Automatic verification (for example, on an emulator)
signInWithPhoneAuthCredential(credential)
}
override fun onVerificationFailed(e: FirebaseException) {
// Error handling
}
override fun onCodeSent(
verificationId: String,
token: PhoneAuthProvider.ForceResendingToken
) {
// Save verificationId to confirm the code
}
})
.build()
PhoneAuthProvider.verifyPhoneNumber(options)
}
After receiving the SMS code, confirm it:
private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {auth.signInWithCredential(credential)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// The user is authorized
val user = task.result?.user
} else {
// Authentication error
}
}
}
โ ๏ธ Attention: Required in production implement:
- ๐ Protection against brute force codes (attempt limit)
- ๐ฑ Checking the phone number format (regular expressions)
- ๐ Mechanism for resending SMS (with delay)
To speed up development, use an Android emulator with a pre-configured phone number (in AVD Manager, select an image with Google Play Services).
4. Creating a chat interface: Jetpack Compose vs XML
The design of the messenger should be intuitive and responsive. We recommend using Jetpack Compose a modern framework for building a UI that simplifies working with dynamic lists (like in chats). An example of the basic structure of the message screen:
@Composablefun ChatScreen(messages: List
) { LazyColumn(modifier = Modifier.fillMaxSize()) {
items(messages) { message ->
MessageBubble(message = message)
}
}
}
@Composable
fun MessageBubble(message: Message) {
val alignment = if (message.isFromCurrentUser) Alignment.End else Alignment.Start
Box(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
contentAlignment = alignment
) {
Card(
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = if (message.isFromCurrentUser) Color.Blue else Color.Gray
)
) {
Text(
text = message.text,
modifier = Modifier.padding(12.dp),
color = Color.White
)
}
}
}
To work with Firebase Realtime Database use ViewModel:
class ChatViewModel : ViewModel() {private val database = Firebase.database
private val messagesRef = database.getReference("messages")
val messages: LiveData
> = liveData {
messagesRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val messageList = snapshot.children.map { it.getValue(Message::class.java) }
emit(messageList.filterNotNull())
}
override fun onCancelled(error: DatabaseError) {
// Error handling
}
})
}
fun sendMessage(text: String) {
val message = Message(
text = text,
timestamp = System.currentTimeMillis(),
senderId = Firebase.auth.currentUser?.uid ?: ""
)
messagesRef.push().setValue(message)
}
}
โ ๏ธ Attention: When using Realtime Database data is transmitted in clear text. To encrypt messages you will need:
- ๐ Implement
end-to-end encryption(for example, using a library LibSignal) - ๐ Store encryption keys only on user devices
- ๐ Document the privacy policy (required for publication in Google Play)
Why shouldn't you use XML for chat?
XML markup becomes unwieldy when dynamically updating a list of messages. Jetpack Compose is better optimized for working with big data and animations, and also automatically handles element reuse (recycling), which is critical for chat performance.
5. Sending media files: photos, videos and documents
To exchange media in WhatsApp-clone you will need:
- Request permission (
READ_EXTERNAL_STORAGE,CAMERA). - Implement file selection via
Intent. - Upload files to Firebase Storage.
- Save a link to the file in Realtime Database.
Example code for loading an image:
private fun uploadImage(uri: Uri) {val storageRef = Firebase.storage.reference
val imagesRef = storageRef.child("chat_images/${System.currentTimeMillis()}.jpg")
val uploadTask = imagesRef.putFile(uri)
uploadTask.addOnSuccessListener {
imagesRef.downloadUrl.addOnSuccessListener { downloadUri ->
// Save the link in the database
val message = Message(
imageUrl = downloadUri.toString(),
senderId = Firebase.auth.currentUser?.uid ?: "",
timestamp = System.currentTimeMillis()
)
Firebase.database.getReference("messages").push().setValue(message)
}
}.addOnFailureListener {
// Error handling
}
}
To take photos directly in the application, use CameraX:
private fun startCamera() {val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder().build()
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
this, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageCapture
)
}, ContextCompat.getMainExecutor(this))
}
โ ๏ธ Attention: When working with media files, consider:
- ๐ธ Resolution
CAMERArequires explanation to the user (starting from Android 6.0). - ๐๏ธ Clear the cache of downloaded files so as not to overload the device memory.
- ๐ For video, use compression (library FFmpeg or
MediaCodec).
To optimize traffic, media files should be compressed before loading. For example, for images you can use the Glide or Coil library with the quality(70) parameter.
6. Push notifications: FCM setting
Notifications of new messages are implemented via Firebase Cloud Messaging (FCM). this:
- Add to
AndroidManifest.xmla service for processing notifications:
<serviceandroid:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
- Create a descendant class
FirebaseMessagingService:
class MyFirebaseMessagingService : FirebaseMessagingService() {override fun onMessageReceived(remoteMessage: RemoteMessage) {
remoteMessage.notification?.let {
showNotification(it.title, it.body)
}
}
private fun showNotification(title: String?, body: String?) {
val intent = Intent(this, ChatActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
}
val pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Message Notifications",
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(channel)
}
notificationManager.notify(0, notificationBuilder.build())
}
companion object {
const val CHANNEL_ID = "messenger_channel"
}
}
To send notifications from the server, use Cloud Functions:
const functions = require('firebase-functions');const admin = require('firebase-admin');
admin.initializeApp();
exports.sendNotification = functions.database.ref('/messages/{messageId}')
.onCreate((snapshot, context) => {
const message = snapshot.val();
if (message.senderId !== message.receiverId) { // Don't send notification to yourself
const payload = {
notification: {
title: "New message",
body: message.text || โYou have been sent a media file,โ
icon: "default"
},
token: userFcmToken // Recipient's Token (must be stored in the database)
};
return admin.messaging().send(payload);
}
return null;
});
โ ๏ธ Attention: Starting from Android 13notifications require explicit user permission. Add to the manifest:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
And request it when you first start:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), REQUEST_CODE)
}
7. Testing and performance optimization
Before publishing the application, test on:
- ๐ฑ Different versions of Android (from
8.0 Oreoto15). - ๐ Different Internet speeds (3G, 4G, Wi-Fi).
- ๐ Different battery levels (turn on saving mode).
- ๐ Large volumes of data (10,000+ messages).
To analyze performance, use Android Profiler v Android Studio:
- Open
View โ Tool Windows โ Profiler. - Run the application and simulate user activity.
- Check:
- ๐ CPU Usage โthere should not be a constant load >50%.
- ๐๏ธ Memory โno leaks.
- ๐ Energy โlow consumption in the background.
Optimize work with Firebase:
- ๐ฅ Use
.indexOnin database rules to speed up queries. - ๐ฆ Limit the number of downloaded messages (
.limitToLast(50)). - ๐ Cache data locally with
Room Database.
โ ๏ธ Attention: If your app consumes >1% battery per hour in the background Google Play it may be blocked for violating the energy efficiency policy Monitor metrics with Play Console.
To test on real devices, use the Firebase Test Lab service - it allows you to run the application on various smartphone models in the cloud.
8. Publishing on Google Play: requirements and pitfalls
Before publishing, check compliance Google Play policies:
- ๐ Age rating (for messengers usually
3+or12+). - ๐ Privacy Policy (required when collecting data).
- ๐ Data security (fill out the form in Play Console).
- ๐ฑ Support
Android 12+(mandatory from 2023).
Prepare the following materials:
| Element | Requirements | Example |
|---|---|---|
| Icon | Size 512ร512, without transparency |
|
| Screenshots | Minimum 2, resolution 1080ร1920 |
Chat screen + registration screen |
| Description | Up to 4000 characters, with keywords | "Fast and secure messenger with encryption..." |
| Video | Not necessary, but increases conversion | Chat demo (15-30 sec) |
โ ๏ธ Attention: If your messenger allows you to send files, indicate restrictions in the description:
- ๐ Maximum file size (for example,
100 MB). - ๐ต Supported formats (for example,
JPEG, PNG, MP4, PDF). - โฑ๏ธ Media storage time on the server (if applicable).
After downloading APK/AAB to Play Console expect moderation from 1 to 3 days. Common reasons for rejection:
- ๐ซ Lack of privacy policy.
- ๐ง Inconsistency with the declared functionality.
- ๐ฑ Application crashes on test devices.
FAQ: Frequently asked questions when developing a messenger
๐น Is it possible to do without a server and use only P2P?
Technically yes - using WebRTC or Bluetooth/Wi-Fi Direct. However, this approach has limitations:
- ๐ถ Users must be online at the same time.
- ๐ It is impossible to send messages offline.
- ๐ฑ It is difficult to implement group chats.
For a full-fledged messenger, a server necessary.
๐น How to implement end-to-end encryption (E2EE) like WhatsApp?
Use the protocol Signal Protocol (open standard). The library LibSignal provides ready-made solutions for Kotlin/Java. Basic steps:
- Generate key pairs (
IdentityKey,SignedPreKey,OneTimePreKey) during registration. - Exchange keys through the server (for example, in encrypted form).
- Encrypt messages using
SessionCipher.
โ ๏ธ Store private keys only on the user's device!
๐น How much will hosting cost for 1000 users?
Approximate calculations for Firebase (tariff Blaze, pay-as-you-go):
- ๐ฅ Authentication: ~$0.01 per 1000 phone number checks.
- ๐๏ธ Realtime Database: ~$1 per 100,000 read/write operations.
- ๐ค Storage: ~$0.026 per GB of stored data.
- ๐ FCM: Free up to 240 messages/min and 5000 notifications/day.
Total: ~$5โ$15/month with moderate activity. For comparison, renting VPS on DigitalOcean will cost ~$10/month, but will require independent assistance. server settings.
๐น Do you need to register the application with Roskomnadzor?
If your messenger:
- ๐ฑ Distributed in Russia.
- ๐๏ธ Stores personal data (phone numbers, names).
- ๐ฅ Has > 100,000 users per day.
Then you are subject to 152-FZ "On Personal Data" and should:
- Register with Roskomnadzor as a PD operator.
- Post the privacy policy in Russian.
- Ensure that data of users from the Russian Federation is stored on servers in Russia.
For small projects (up to 100k users), registration is not required, but a privacy policy is required.
๐น How to add support for multiple languages?
Use the standard localization mechanism in Android:
- Create folders
values-es,values-fretc. inres/. - Place translated strings there (for example,
strings.xml). - Load translations via
getString(R.string.message).
To dynamically change the language without restarting the application, use:
val config = Configuration(resources.configuration)config.setLocale(Locale("es")) // Spanish
resources.updateConfiguration(config, resources.displayMetrics)
โ ๏ธ Donโt forget to translate not only the UI, but also notifications, errors and system messages!