Xây dựng ứng dụng Chat Real-time đơn giản với Spring Boot và Android WebSocket (Phần 1)
Chào các bạn, trong chuỗi bài viết này, mình sẽ hướng dẫn các bạn cách xây dựng một hệ thống WebSocket từ cơ bản đến nâng cao.
Phần 1 này chúng ta sẽ tập trung vào việc tạo ra một ứng dụng Chat đơn giản: Server sẽ nhận tin nhắn từ một người và gửi (broadcast) cho tất cả những người đang kết nối.
1. Phía Server: Spring Boot
Bước 1: Khởi tạo Project
Bạn truy cập Spring Initializr và chọn các cấu hình sau:
- Project: Maven
- Language: Java 17+
- Dependencies:
Spring Web,WebSocket,Lombok.
Bước 2: Thêm Dependency vào pom.xml
Nếu bạn đã tạo project, hãy đảm bảo có dependency này:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Bước 3: Tạo Model tin nhắn (ChatMessage.java)
Chúng ta dùng class này để chứa dữ liệu chat.
import lombok.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ChatMessage {
private String sender;
private String content;
private String timestamp;
}
Bước 4: Tạo Handler xử lý logic (ChatWebSocketHandler.java)
Đây là nơi "đầu não" xử lý việc kết nối và gửi tin nhắn.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.List;
public class ChatWebSocketHandler extends TextWebSocketHandler {
// Danh sách lưu trữ các session đang kết nối
private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
System.out.println("Mới có người vào: " + session.getId());
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
System.out.println("Nhận tin nhắn: " + payload);
// Gửi tin nhắn này cho TẤT CẢ mọi người (Broadcast)
for (WebSocketSession s : sessions) {
if (s.isOpen()) {
s.sendMessage(new TextMessage(payload));
}
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session);
System.out.println("Có người rời đi: " + session.getId());
}
}
Bước 5: Cấu hình WebSocket (WebSocketConfig.java)
Đăng ký endpoint để client có thể kết nối tới.
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.*;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatWebSocketHandler(), "/ws/chat")
.setAllowedOrigins("*"); // Cho phép mọi nguồn kết nối
}
}
2. Phía Client: Android (Java)
Bước 1: Thêm quyền và thư viện
Mở AndroidManifest.xml, thêm quyền Internet:
<uses-permission android:name="android.permission.INTERNET" />
Mở build.gradle (Module: app), thêm thư viện OkHttp:
implementation("com.squareup.okhttp3:okhttp:4.10.0")
Bước 2: Giao diện đơn giản (activity_main.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Status: Disconnected" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvChat"
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="horizontal">
<EditText
android:id="@+id/edtMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Nhập tin nhắn..." />
<Button
android:id="@+id/btnSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Gửi" />
</LinearLayout>
</LinearLayout>
Bước 3: Kết nối WebSocket (MainActivity.java)
import android.os.Bundle;
import android.widget.*;
import androidx.appcompat.app.AppCompatActivity;
import okhttp3.*;
import okio.ByteString;
public class MainActivity extends AppCompatActivity {
private WebSocket webSocket;
private TextView tvStatus;
private EditText edtMessage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvStatus = findViewById(R.id.tvStatus);
edtMessage = findViewById(R.id.edtMessage);
Button btnSend = findViewById(R.id.btnSend);
startWebSocket();
btnSend.setOnClickListener(v -> {
String msg = edtMessage.getText().toString();
if (!msg.isEmpty()) {
// Gửi tin nhắn dưới dạng JSON (đơn giản hóa)
webSocket.send("{\"sender\":\"Android\", \"content\":\"" + msg + "\"}");
edtMessage.setText("");
}
});
}
private void startWebSocket() {
OkHttpClient client = new OkHttpClient();
// THAY ĐỔI IP NÀY THÀNH IP MÁY TÍNH CỦA BẠN
Request request = new Request.Builder().url("ws://192.168.1.5:8080/ws/chat").build();
webSocket = client.newWebSocket(request, new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
runOnUiThread(() -> tvStatus.setText("Status: Connected"));
}
@Override
public void onMessage(WebSocket webSocket, String text) {
runOnUiThread(() -> {
// Ở đây bạn nên parse JSON và add vào RecyclerView
Toast.makeText(MainActivity.this, "Nhận: " + text, Toast.LENGTH_SHORT).show();
});
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
runOnUiThread(() -> tvStatus.setText("Status: Error - " + t.getMessage()));
}
});
}
}
3. Cách chạy thử
- Chạy Server: Chạy project Spring Boot từ IntelliJ.
- Tìm IP máy tính: Mở CMD gõ
ipconfig(Windows) hoặc Terminal gõifconfig(Mac/Linux) để lấy địa chỉ IP LAN (ví dụ192.168.x.x). - Cập nhật IP trong Android: Thay đổi URL trong
MainActivity.java. - Chạy App Android: Mở máy ảo hoặc cắm dây điện thoại vào chạy.
Kết quả: Khi bạn gõ tin nhắn trên Android và bấm Gửi, Server sẽ nhận được và gửi ngược lại cho chính bạn (và tất cả những ai đang kết nối).
4. Tổng kết
Vậy là chúng ta đã hoàn thành một ứng dụng Chat WebSocket cơ bản nhất. Tuy nhiên, cách làm này có một nhược điểm lớn: Nếu chúng ta muốn thêm tính năng "Tạo phòng", "Gửi ảnh", "Xác thực người dùng"... thì code trong handleTextMessage sẽ cực kỳ rắc rối.
Hẹn gặp lại các bạn ở Phần 2, mình sẽ hướng dẫn cách áp dụng Strategy Pattern để thiết kế một hệ thống WebSocket cực kỳ chuyên nghiệp và dễ mở rộng!
All rights reserved